/* (c) 2016 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geogig.geoserver.config;
import static com.google.common.base.Preconditions.checkState;
import static java.lang.String.format;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geotools.util.logging.Logging;
import org.locationtech.geogig.repository.Repository;
import org.locationtech.geogig.repository.RepositoryResolver;
import com.google.common.base.Throwables;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
class RepositoryCache {
private static final Logger LOGGER = Logging.getLogger(RepositoryCache.class);
private final LoadingCache<String, Repository> repoCache;
public RepositoryCache(final RepositoryManager repoManager) {
RemovalListener<String, Repository> disposingListener = new RemovalListener<String, Repository>() {
@Override
public void onRemoval(RemovalNotification<String, Repository> notification) {
String repoId = notification.getKey();
Repository repository = notification.getValue();
if (repository != null) {
try {
URI location = repository.getLocation();
LOGGER.fine(format("Closing cached GeoGig repository instance %s",
location != null ? location : repoId));
repository.close();
LOGGER.finer(format("Closed cached GeoGig repository instance %s",
location != null ? location : repoId));
} catch (RuntimeException e) {
LOGGER.log(Level.WARNING, format(
"Error disposing GeoGig repository instance for id %s", repoId), e);
}
}
}
};
final CacheLoader<String, Repository> loader = new CacheLoader<String, Repository>() {
private final RepositoryManager manager = repoManager;
@Override
public Repository load(final String repoId) throws Exception {
try {
RepositoryInfo repoInfo = manager.get(repoId);
URI repoLocation = repoInfo.getLocation();
// RepositoryResolver.load returns an open repository or fails
Repository repo = RepositoryResolver.load(repoLocation);
checkState(repo.isOpen());
return repo;
} catch (Exception e) {
LOGGER.log(Level.WARNING,
format("Error loading GeoGig repository instance for id %s", repoId),
e);
throw e;
}
}
};
repoCache = CacheBuilder.newBuilder()//
.softValues()//
.expireAfterAccess(5, TimeUnit.MINUTES)//
.removalListener(disposingListener)//
.build(loader);
}
/**
* @implNote: the returned repository's close() method does nothing. Closing the repository
* happens when it's evicted from the cache or is removed. This avoids several errors
* as GeoSever can aggressively create and dispose DataStores, whose dispose() method
* would otherwise close the repository and produce unexpected exceptions for any
* other code using it.
*/
public Repository get(final String repositoryId) throws IOException {
try {
return repoCache.get(repositoryId);
} catch (ExecutionException e) {
Throwables.propagateIfInstanceOf(e.getCause(), IOException.class);
throw new IOException("Error obtaining cached geogig instance for repo " + repositoryId,
e.getCause());
}
}
public void invalidate(final String repoId) {
repoCache.invalidate(repoId);
}
public void invalidateAll() {
repoCache.invalidateAll();
}
}