com.mchange.v3.filecache.FileCache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.servicemix.bundles.mchange-commons-java
Show all versions of org.apache.servicemix.bundles.mchange-commons-java
This OSGi bundle wraps ${pkgArtifactId} ${pkgVersion} jar file.
/*
* Distributed as part of mchange-commons-java 0.2.11
*
* Copyright (C) 2015 Machinery For Change, Inc.
*
* Author: Steve Waldman
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of EITHER:
*
* 1) The GNU Lesser General Public License (LGPL), version 2.1, as
* published by the Free Software Foundation
*
* OR
*
* 2) The Eclipse Public License (EPL), version 1.0
*
* You may choose which license to accept if you wish to redistribute
* or modify this work. You may offer derivatives of this work
* under the license you have chosen, or you may provide the same
* choice of license which you have been offered here.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received copies of both LGPL v2.1 and EPL v1.0
* along with this software; see the files LICENSE-EPL and LICENSE-LGPL.
* If not, the text of these licenses are currently available at
*
* LGPL v2.1: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* EPL v1.0: http://www.eclipse.org/org/documents/epl-v10.php
*
*/
package com.mchange.v3.filecache;
import java.io.*;
import java.net.*;
import java.util.*;
import com.mchange.v2.io.*;
import com.mchange.v2.log.*;
import com.mchange.v1.io.InputStreamUtils;
import com.mchange.v1.io.OutputStreamUtils;
public final class FileCache
{
final static MLogger logger = MLog.getLogger( FileCache.class );
final File cacheDir;
final int buffer_size;
final boolean read_only;
final List fetchers;
private InputStream fetchURL(URL u) throws IOException
{
List exceptions = null;
for (URLFetcher fetcher : fetchers)
{
try { return fetcher.openStream( u, logger ); }
catch (FileNotFoundException e)
{ throw e; }
catch (IOException e)
{
if (logger.isLoggable( MLevel.FINE ))
logger.log( MLevel.FINE, "URLFetcher " + fetcher + " failed on Exception. Will try next fetcher, if any.", e);
if (exceptions == null)
exceptions = new LinkedList();
exceptions.add(e);
}
}
if (logger.isLoggable( MLevel.WARNING ))
{
logger.log( MLevel.WARNING, "All URLFetchers failed on URL " + u);
for (int i = 0, len = exceptions.size(); i < len; ++i)
logger.log(MLevel.WARNING, "URLFetcher Exception #" + (i+1), exceptions.get(i));
}
// hopefully we returned long before we reached here...
throw new IOException("Failed to fetch URL '" + u + "'.");
}
public FileCache(File cacheDir, int buffer_size, boolean read_only) throws IOException
{ this( cacheDir, buffer_size, read_only, Collections.singletonList( (URLFetcher) URLFetchers.DEFAULT ) ); }
public FileCache(File cacheDir, int buffer_size, boolean read_only, URLFetcher... fetchers) throws IOException
{ this( cacheDir, buffer_size, read_only, Arrays.asList( fetchers ) ); }
public FileCache(File cacheDir, int buffer_size, boolean read_only, List fetchers) throws IOException
{
this.cacheDir = cacheDir;
this.buffer_size = buffer_size;
this.read_only = read_only;
this.fetchers = Collections.unmodifiableList( fetchers );
if (cacheDir.exists())
{
if (! cacheDir.isDirectory())
loggedIOException(MLevel.SEVERE, cacheDir + "exists and is not a directory. Can't use as cacheDir.");
else if (! cacheDir.canRead() )
loggedIOException(MLevel.SEVERE, cacheDir + "must be readable.");
else if (! cacheDir.canWrite() && ! read_only)
loggedIOException(MLevel.SEVERE, cacheDir + "not writable, and not read only.");
}
else if (! cacheDir.mkdir())
loggedIOException(MLevel.SEVERE, cacheDir + "does not exist and could not be created.");
//okay... we have a cacheDir
}
public void ensureCached(FileCacheKey key, boolean force_reacquire) throws IOException
{
File f = file( key );
if (! read_only )
{
if ( force_reacquire || (!f.exists()) )
{
InputStream is = null;
OutputStream os = null;
try
{
if ( logger.isLoggable( MLevel.FINE ) )
logger.log( MLevel.FINE, "Caching file for " + key + " to " + f.getAbsolutePath() + "...");
File dir = f.getParentFile();
if (! dir.exists()) dir.mkdirs();
//is = new BufferedInputStream( key.getURL().openStream(), buffer_size );
is = new BufferedInputStream( fetchURL( key.getURL() ), buffer_size );
os = new BufferedOutputStream( new FileOutputStream( f ), buffer_size );
for (int b = is.read(); b >= 0; b = is.read())
os.write(b);
if ( logger.isLoggable( MLevel.INFO ) )
logger.log( MLevel.INFO, "Cached file for " + key + ".");
}
catch (IOException e)
{
logger.log( MLevel.WARNING, "An exception occurred while caching file for " + key + ". Deleting questionable cached file.", e);
f.delete();
throw e;
}
finally
{
InputStreamUtils.attemptClose( is );
OutputStreamUtils.attemptClose( os );
}
}
else
{
if ( logger.isLoggable( MLevel.FINE ) )
logger.log( MLevel.FINE, "File for " + key + " already exists and force_reacquire is not set.");
}
}
else if ( force_reacquire )
{
String message = "force_reacquire canot be set on a read_only FileCache.";
IllegalArgumentException e = new IllegalArgumentException( message );
logger.log( MLevel.WARNING, message, e );
throw e;
}
else if (! f.exists() )
{
String message = "Cache is read only, and file for key '" + key + "' does not exist.";
FileNotCachedException e = new FileNotCachedException( message );
logger.log( MLevel.FINE, message, e );
throw e;
}
}
public InputStream fetch(FileCacheKey key, boolean force_reacquire) throws IOException
{
ensureCached( key, force_reacquire);
return new FileInputStream( file(key) );
}
public boolean isCached(FileCacheKey key) throws IOException
{ return file( key ).exists(); }
final static FileFilter NOT_DIR_FF = new FileFilter()
{
public boolean accept(File f)
{ return ! f.isDirectory(); }
};
static class NotDirAndFileFilter implements FileFilter
{
FileFilter ff;
NotDirAndFileFilter(FileFilter ff)
{ this.ff = ff; }
public boolean accept(File f)
{ return (! f.isDirectory()) && ff.accept(f); }
}
public int countCached() throws IOException
{
int count = 0;
for( FileIterator fi = DirectoryDescentUtils.depthFirstEagerDescent( cacheDir, NOT_DIR_FF, false ); fi.hasNext(); )
{
fi.next();
++count;
}
return count;
}
public int countCached(FileFilter filter) throws IOException
{
int count = 0;
for( FileIterator fi = DirectoryDescentUtils.depthFirstEagerDescent( cacheDir, new NotDirAndFileFilter( filter ), false ); fi.hasNext(); )
{
fi.next();
++count;
}
return count;
}
public File fileForKey(FileCacheKey key)
{ return file(key); }
private File file(FileCacheKey key)
{ return new File( cacheDir, key.getCacheFilePath() ); }
private void loggedIOException(MLevel lvl, String msg) throws IOException
{
IOException e = new IOException( msg );
logger.log(lvl, msg, e);
throw e;
}
}