blob: f46f496b97c22410d997e66677660742820cb67c [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.maven.mercury.repository.cache.fs;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.maven.mercury.artifact.ArtifactCoordinates;
import org.apache.maven.mercury.artifact.ArtifactMetadata;
import org.apache.maven.mercury.event.EventManager;
import org.apache.maven.mercury.event.EventTypeEnum;
import org.apache.maven.mercury.event.GenericEvent;
import org.apache.maven.mercury.event.MercuryEventListener;
import org.apache.maven.mercury.repository.api.MetadataCacheException;
import org.apache.maven.mercury.repository.api.MetadataCorruptionException;
import org.apache.maven.mercury.repository.api.RepositoryGAMetadata;
import org.apache.maven.mercury.repository.api.RepositoryGAVMetadata;
import org.apache.maven.mercury.repository.api.RepositoryMetadataCache;
import org.apache.maven.mercury.repository.api.RepositoryUpdatePolicy;
import org.apache.maven.mercury.repository.local.m2.ArtifactLocation;
import org.apache.maven.mercury.util.FileLockBundle;
import org.apache.maven.mercury.util.FileUtil;
import org.apache.maven.mercury.util.TimeUtil;
import org.codehaus.plexus.lang.DefaultLanguage;
import org.codehaus.plexus.lang.Language;
/**
* @author Oleg Gusakov
* @version $Id$
*/
public class MetadataCacheFs
implements RepositoryMetadataCache
{
public static final String EVENT_FIND_GA = "find.ga";
public static final String EVENT_FIND_GAV = "find.gav";
public static final String EVENT_FIND_RAW = "find.raw";
public static final String EVENT_UPDATE_GA = "update.ga";
public static final String EVENT_UPDATE_GAV = "update.gav";
public static final String EVENT_SAVE_RAW = "save.raw";
// public static final String SYSTEM_PROPERTY_CACHE_METADATA = "mercury.cache.metadata";
//
// /** by default - cache metadata in memory */
// private static final boolean cacheMetadata = Boolean.valueOf( System.getProperty( SYSTEM_PROPERTY_CACHE_METADATA, "true" ) );
public static final String SYSTEM_PROPERTY_CACHE_RAW = "mercury.cache.raw";
/** by default - do not cache raw data in memory to preserve RAM */
private static final boolean cacheRaw = Boolean.valueOf( System.getProperty( SYSTEM_PROPERTY_CACHE_RAW, "false" ) );
private static final Language LANG = new DefaultLanguage( RepositoryGAVMetadata.class );
static volatile Map<String, MetadataCacheFs> fsCaches =
Collections.synchronizedMap( new HashMap<String, MetadataCacheFs>( 2 ) );
// store resolved cached data in memory
private volatile Map<String, RepositoryGAMetadata> gaCache =
(Map<String, RepositoryGAMetadata>) Collections.synchronizedMap( new HashMap<String, RepositoryGAMetadata>( 512 ) );
private volatile Map<String, RepositoryGAVMetadata> gavCache =
(Map<String, RepositoryGAVMetadata>) Collections.synchronizedMap( new HashMap<String, RepositoryGAVMetadata>( 1024 ) );
private volatile Map<String, byte[]> rawCache = cacheRaw
? (Map<String, byte[]>) Collections.synchronizedMap( new HashMap<String, byte[]>( 1024 ) )
: null
;
private File root;
private EventManager _eventManager;
/**
* access to all known FS caches
*
* @param root
* @return
* @throws IOException
*/
public static MetadataCacheFs getCache( File root )
throws IOException
{
if ( root == null || ( root.exists() && root.isFile() ) )
throw new IllegalArgumentException( LANG.getMessage( "bad.root.file", root == null ? "null"
: root.getAbsolutePath() ) );
String key = root.getCanonicalPath();
MetadataCacheFs fsc = fsCaches.get( key );
if ( fsc == null )
{
fsc = new MetadataCacheFs( root );
fsCaches.put( key, fsc );
}
return fsc;
}
/**
* private as it should be obtained via a call to <code>getCache()</code>
*/
private MetadataCacheFs( File root )
{
this.root = root;
}
public RepositoryGAMetadata findGA( String repoGuid, RepositoryUpdatePolicy up, ArtifactCoordinates coord )
throws MetadataCorruptionException
{
GenericEvent event = null;
try
{
String gaKey = getGAKey( coord );
if ( _eventManager != null )
event = new GenericEvent( EventTypeEnum.fsCache, EVENT_FIND_GA, gaKey );
RepositoryGAMetadata inMem = gaCache.get( gaKey );
if ( inMem != null )
{
// im-memory never expires!
/*
* long lastCheckMillis = inMem.getLastCheckMillis(); if ( up.timestampExpired( lastCheckMillis, null )
* ) { inMem.setExpired( true ); gaCache.put( gaKey, inMem ); }
*/
if ( _eventManager != null )
event.setResult( "found in memory, expired is " + inMem.isExpired() );
return inMem;
}
// don't mess with cache if we are past update threshold
long now = TimeUtil.getUTCTimestampAsLong();
if ( up.timestampExpired( now, null ) )
{
if ( event != null )
event.setResult( LANG.getMessage( "pass.update" ) );
return null;
}
File gaDir = getGADir( coord );
File gamF = getGAFile( gaDir, repoGuid );
CachedGAMetadata md = null;
if ( gamF.exists() )
{
md = new CachedGAMetadata( gamF );
long lastCheckMillis = md.getLastCheckMillis();
if ( up != null && up.timestampExpired( lastCheckMillis, null ) )
md.setExpired( true );
gaCache.put( gaKey, md );
if ( _eventManager != null )
event.setResult( "found on disk, expired is " + md.isExpired() );
}
else
{
if ( _eventManager != null )
event.setResult( "not found" );
}
return md;
}
catch ( Exception e )
{
throw new MetadataCorruptionException( e.getMessage() );
}
finally
{
if ( _eventManager != null )
{
event.stop();
_eventManager.fireEvent( event );
}
}
}
public RepositoryGAVMetadata findGAV( String repoGuid, RepositoryUpdatePolicy up, ArtifactCoordinates coord )
throws MetadataCorruptionException
{
FileLockBundle lock = null;
GenericEvent event = null;
try
{
String gavKey = getGAVKey( coord );
if ( _eventManager != null )
event = new GenericEvent( EventTypeEnum.fsCache, EVENT_FIND_GAV, gavKey );
RepositoryGAVMetadata inMem = gavCache.get( gavKey );
if ( inMem != null )
{
/*
* long lastCheckMillis = inMem.getLastCheckMillis(); if ( up.timestampExpired( lastCheckMillis, null )
* ) { inMem.setExpired( true ); gavCache.put( gavKey, inMem ); }
*/
if ( _eventManager != null )
event.setResult( "found in memory, expired is " + inMem.isExpired() );
return inMem;
}
// don't mess with cache if we are past update threshold
long now = TimeUtil.getUTCTimestampAsLong();
if ( up.timestampExpired( now, null ) )
{
if ( event != null )
event.setResult( LANG.getMessage( "pass.update" ) );
return null;
}
File gavDir = getGAVDir( coord );
lock = FileUtil.lockDir( gavDir.getCanonicalPath(), 500L, 5L );
File gavmF = getGAVFile( gavDir, repoGuid );
CachedGAVMetadata md = null;
if ( gavmF.exists() )
{
md = new CachedGAVMetadata( gavmF );
if ( up != null && up.timestampExpired( md.getLastCheck(), null ) )
md.setExpired( true );
if ( _eventManager != null )
event.setResult( "found on disk, expired is " + inMem.isExpired() );
gavCache.put( gavKey, md );
}
else if ( _eventManager != null )
event.setResult( "not found" );
return md;
}
catch ( Exception e )
{
throw new MetadataCorruptionException( e.getMessage() );
}
finally
{
if ( lock != null )
lock.release();
if ( _eventManager != null )
{
event.stop();
_eventManager.fireEvent( event );
}
}
}
public void updateGA( String repoGuid, RepositoryGAMetadata gam )
throws MetadataCacheException
{
FileLockBundle lock = null;
GenericEvent event = null;
try
{
String gaKey = getGAKey( gam.getGA() );
if ( _eventManager != null )
event = new GenericEvent( EventTypeEnum.fsCache, EVENT_UPDATE_GA, gaKey );
File gaDir = getGADir( gam.getGA() );
lock = FileUtil.lockDir( gaDir.getCanonicalPath(), 500L, 5L );
File gamF = getGAFile( gaDir, repoGuid );
CachedGAMetadata md = new CachedGAMetadata( gam );
md.cm.save( gamF );
gaCache.put( gaKey, md );
}
catch ( Exception e )
{
throw new MetadataCacheException( e.getMessage() );
}
finally
{
if ( lock != null )
lock.release();
if ( _eventManager != null )
{
event.stop();
_eventManager.fireEvent( event );
}
}
}
public void updateGAV( String repoGuid, RepositoryGAVMetadata gavm )
throws MetadataCacheException
{
FileLockBundle lock = null;
GenericEvent event = null;
try
{
String gavKey = getGAKey( gavm.getGAV() );
if ( _eventManager != null )
event = new GenericEvent( EventTypeEnum.fsCache, EVENT_UPDATE_GA, gavKey );
File gavDir = getGAVDir( gavm.getGAV() );
lock = FileUtil.lockDir( gavDir.getCanonicalPath(), 500L, 5L );
File gavmF = getGAVFile( gavDir, repoGuid );
CachedGAVMetadata md = new CachedGAVMetadata( gavm );
md.cm.save( gavmF );
gavCache.put( gavKey, md );
}
catch ( Exception e )
{
throw new MetadataCacheException( e.getMessage() );
}
finally
{
if ( lock != null )
lock.release();
if ( _eventManager != null )
{
event.stop();
_eventManager.fireEvent( event );
}
}
}
public byte[] findRaw( ArtifactMetadata bmd )
throws MetadataCacheException
{
GenericEvent event = null;
try
{
String rawKey = bmd.getGAV();
if ( _eventManager != null )
event = new GenericEvent( EventTypeEnum.fsCache, EVENT_FIND_RAW, rawKey );
byte[] res = cacheRaw
? rawCache.get( rawKey )
: null
;
if ( res != null )
{
if ( _eventManager != null )
event.setResult( "found in memory" );
return res;
}
// locking is provided by underlying OS, don't waste the effort
File f =
new File( getGAVDir( bmd.getEffectiveCoordinates() ), bmd.getArtifactId() + FileUtil.DASH
+ bmd.getVersion() + "." + bmd.getType() );
if ( !f.exists() )
return null;
res = FileUtil.readRawData( f );
if( cacheRaw)
rawCache.put( rawKey, res );
if ( _eventManager != null )
event.setResult( "found on disk" );
return res;
}
catch ( IOException e )
{
throw new MetadataCacheException( e.getMessage() );
}
finally
{
if ( _eventManager != null )
{
event.stop();
_eventManager.fireEvent( event );
}
}
}
public void saveRaw( ArtifactMetadata md, byte[] rawBytes )
throws MetadataCacheException
{
GenericEvent event = null;
// locking is provided by underlying OS, don't waste the effort
try
{
String rawKey = md.getGAV();
if ( _eventManager != null )
event = new GenericEvent( EventTypeEnum.fsCache, EVENT_SAVE_RAW, rawKey );
if( cacheRaw )
rawCache.put( rawKey, rawBytes );
File f =
new File( getGAVDir( md.getEffectiveCoordinates() ), md.getArtifactId() + FileUtil.DASH
+ md.getVersion() + "." + md.getType() );
FileUtil.writeRawData( f, rawBytes );
}
catch ( IOException e )
{
throw new MetadataCacheException( e.getMessage() );
}
finally
{
if ( _eventManager != null )
{
event.stop();
_eventManager.fireEvent( event );
}
}
}
// ---------------------------------------------------------------------------------------
private String getGAKey( ArtifactCoordinates coord )
{
return coord.getGroupId() + ":" + coord.getArtifactId();
}
private String getGAVKey( ArtifactCoordinates coord )
{
return coord.getGroupId() + ":" + coord.getArtifactId() + ":" + coord.getVersion();
}
private File getGADir( ArtifactCoordinates coord )
{
File dir = new File( root, coord.getGroupId() + FileUtil.SEP + coord.getArtifactId() );
if ( !dir.exists() )
dir.mkdirs();
return dir;
}
private File getGAFile( File gaDir, String repoGuid )
{
return new File( gaDir, "meta-ga-" + repoGuid + ".xml" );
}
private File getGAVDir( ArtifactCoordinates coord )
{
String version = ArtifactLocation.calculateVersionDir( coord.getVersion() );
File dir = new File( getGADir( coord ), coord.getArtifactId() + FileUtil.DASH + version );
if ( !dir.exists() )
dir.mkdirs();
return dir;
}
private File getGAVFile( File gavDir, String repoGuid )
{
return new File( gavDir, "meta-gav-" + repoGuid + ".xml" );
}
public void register( MercuryEventListener listener )
{
if ( _eventManager == null )
_eventManager = new EventManager();
_eventManager.register( listener );
}
public void unRegister( MercuryEventListener listener )
{
if ( _eventManager != null )
_eventManager.unRegister( listener );
}
public void setEventManager( EventManager eventManager )
{
if ( _eventManager == null )
_eventManager = eventManager;
else
_eventManager.getListeners().addAll( eventManager.getListeners() );
}
public void clearSession()
throws MetadataCacheException
{
if( cacheRaw )
rawCache.clear();
gaCache.clear();
gavCache.clear();
}
}