/**
* Copyright 2015 Palantir Technologies, Inc.
*
* 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.
*/
package com.palantir.giraffe.file.base;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystemAlreadyExistsException;
import java.nio.file.FileSystemNotFoundException;
import java.util.Map;
import java.util.concurrent.ExecutionException;
/**
* A {@link BaseFileSystemProvider} that caches created file systems, allowing
* at most one open file system for a given URI at a time. A new file system can
* be created for a given URI after the previous one is closed.
*
* @author bkeyes
*
* @param <P> the type of path used by the provider's file systems
*/
public abstract class CachingBaseFileSystemProvider<P extends BasePath<P>> extends
BaseFileSystemProvider<P> {
private final SystemCache<Object, BaseFileSystem<P>> cache = new SystemCache<>();
protected CachingBaseFileSystemProvider(Class<P> pathClass) {
super(pathClass);
}
@Override
public final FileSystem newFileSystem(final URI uri, final Map<String, ?> env)
throws IOException {
Object key = uriToCacheKey(checkUri(uri));
BaseFileSystem<P> fs = cache.init(key, new SystemCache.Factory<BaseFileSystem<P>>() {
@Override
public BaseFileSystem<P> newSystem() throws IOException {
return openFileSystem(uri, env);
}
});
if (fs != null) {
return fs;
} else {
throw new FileSystemAlreadyExistsException();
}
}
/**
* Creates a new, open file system. The file system returned by this method
* is cached until it is closed.
*
* @param uri a valid file system URI
* @param env a possible empty map of provider specific properties
*
* @return a new file system
*
* @throws IllegalArgumentException if {@code env} does the contain require
* properties or if a property value is invalid
* @throws IOException if an I/O error occurs creating the file system
*/
protected abstract BaseFileSystem<P> openFileSystem(URI uri, Map<String, ?> env)
throws IOException;
@Override
public final FileSystem getFileSystem(URI uri) {
Object key = uriToCacheKey(checkUri(uri));
FileSystem fs = null;
try {
fs = cache.get(key);
} catch (ExecutionException e) {
// the actual error is reported in the thread calling newFileSystem
throw new FileSystemNotFoundException(uri.toString() + " (creation error)");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new FileSystemNotFoundException(uri.toString() + " (interruption)");
}
if (fs == null) {
throw new FileSystemNotFoundException(uri.toString());
} else {
return fs;
}
}
@Override
void fileSystemClosed(BaseFileSystem<P> fileSystem) {
cache.remove(uriToCacheKey(fileSystem.uri()));
}
/**
* Checks that {@code uri} is a valid URI for a file system from this
* provider.
*
* @param uri the URI to check
*
* @return the given URI
*
* @throws IllegalArgumentException if the URI is invalid
*/
protected abstract URI checkUri(URI uri);
/**
* Converts {@code uri} into a cache key. By default, this method returns
* the given URI.
*/
protected Object uriToCacheKey(URI uri) {
return uri;
}
}