/**
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @author Arne Kepp / The Open Planning Project 2009
*
*/
package org.geowebcache.storage;
import java.io.UncheckedIOException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geowebcache.filter.parameters.ParameterFilter;
import org.geowebcache.filter.parameters.ParametersUtils;
import org.geowebcache.layer.TileLayer;
/**
* Manages the persistence of the actual data contained in cacheable objects (tiles, WFS responses).
* <p>
* Blobstores may assume that the StorageObjects passed to them are completely filled in except for
* the blob fields.
* </p>
*/
public interface BlobStore {
static Log log = LogFactory.getLog(BlobStore.class);
/**
* Delete the cache for the named layer
*
* @param layerName the name of the layer to delete
* @return {@literal true} if successful, {@literal false} otherwise
*/
public boolean delete(String layerName) throws StorageException;
/**
* Delete the cache for the named gridset and layer
*
* @param layerName
* @param gridSetId
* @return {@literal true} if successful, {@literal false} otherwise
* @throws StorageException
*/
public boolean deleteByGridsetId(final String layerName, final String gridSetId)
throws StorageException;
/**
* Delete the cache for the named layer and parameters.
*
* @param layerName
* @param parameters Complete filtered parameters to generate the ID
* @return {@literal true} if successful, {@literal false} otherwise
* @throws StorageException
*/
public default boolean deleteByParameters(final String layerName, final Map<String, String> parameters)
throws StorageException {
return deleteByParametersId(layerName, ParametersUtils.getId(parameters));
}
/**
* Delete the cache for the named layer and parameters id.
*
* @param layerName
* @param parameters
* @return {@literal true} if successful, {@literal false} otherwise
* @throws StorageException
*/
public boolean deleteByParametersId(final String layerName, String parametersId)
throws StorageException;
/**
* Delete the cached blob associated with the specified TileObject.
* The passed in object itself will not be modified.
*
* @param obj
* @return {@literal true} if successful, {@literal false} otherwise
* @throws StorageException
*/
public boolean delete(TileObject obj) throws StorageException;
/**
* Delete the cached blob associated with the tiles in the given range.
*
* @param obj the range of tiles.
* @return {@literal true} if successful, {@literal false} otherwise
* @throws StorageException
*/
public boolean delete(TileRange obj) throws StorageException;
/**
* Retrieves a tile from the storage, filling its metadata too
* @param obj
* @return {@literal true} if successful, {@literal false} otherwise
* @throws StorageException
*/
public boolean get(TileObject obj) throws StorageException;
/**
* Store blob. Calls getBlob() on passed object, does not modify the object.
*
* @param key
* @param blog
* @throws StorageException
*/
public void put(TileObject obj) throws StorageException;
/**
* Wipes the entire storage. Should only be invoked during testing.
*
* @throws StorageException
*/
@Deprecated
public void clear() throws StorageException;
/**
* Destroy method for Spring
*/
public void destroy();
/**
* Add an event listener
* @param listener
* @see BlobStoreListener
*/
public void addListener(BlobStoreListener listener);
/**
* Remove an event listener
* @param listener
* @return {@literal true} if successful, {@literal false} otherwise
* @see BlobStoreListener
*/
public boolean removeListener(BlobStoreListener listener);
/**
* Get the cached parameter maps for a layer
* @param layerName
* @return
*/
public default Set<Map<String, String>> getParameters(String layerName) throws StorageException {
return getParametersMapping(layerName).values().stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toSet());
}
/**
* Get the IDs of the cached parameter maps for a layer.
*
* <p>Stores that predate 1.12 should implement this to provide any parameters for which maps
* are not available. Stores not using {@link ParametersUtils.getId} or which have more
* efficient ways to provide it should also implement it.
* @param layerName
* @return
*/
public default Set<String> getParameterIds(String layerName) throws StorageException {
return getParametersMapping(layerName).keySet();
}
/**
* Rename a stored layer
* @param oldLayerName the old name of the layer
* @param newLayerName the new name
* @return {@literal true} if successful or the layer didn't exist, {@literal false} otherwise
* @throws StorageException if {@code newLayerName} already exists or the rename can't be accomplished for other reason
*/
public boolean rename(String oldLayerName, String newLayerName) throws StorageException;
/**
* @return the value of the stored metadata for the given layer and key, or {@code null} if no
* such value exists
*/
public String getLayerMetadata(String layerName, String key);
/**
* Stores a metadata key/value pair for the given layer
*/
public void putLayerMetadata(String layerName, String key, String value);
/**
* @return {@code true} if the blobstore has a layer named {@code layerName}
* in use, {@code false} otherwise
*/
public boolean layerExists(String layerName);
/**
* Gets the mapping from parameter IDs to parameter maps. For cache parameter IDs that lack
* reverse mappings (as produced by GWC before 1.12) the ID will map to an empty
* {@link Optional}.
* @param layerName The layer to look up.
* @return A map from parameter IDs to {@link Optional}s containing parameter maps, or empty
* {@link Optional}s.
* @since 1.12
*/
Map<String,Optional<Map<String, String>>> getParametersMapping(String layerName);
/**
* If the given layer is cached, remove
* @param layer
* @throws StorageException
*/
public default boolean purgeOrphans(TileLayer layer) throws StorageException {
// TODO maybe do purging based on gridset and format
try{
final List<ParameterFilter> parameterFilters = layer.getParameterFilters();
// Given known parameter mapping, figures out if the parameters need to be purged
final Function<Map<String,String>, Boolean> parametersNeedPurge = parameters-> {
return parameters.size() != parameterFilters.size() || // Should have the same number of parameters as the layer has filters
parameterFilters.stream()
.allMatch(pfilter -> { // Do all the parameter filters on the layer consider their parameter legal
final String key = pfilter.getKey();
final String value = parameters.get(key);
if(Objects.isNull(value)) {
return true; // No parameter for this filter so purge
}
return !pfilter.isFilteredValue(value); // purge if it's not a filtered value
});
};
return getParametersMapping(layer.getName()).entrySet().stream()
.filter(parameterMapping -> {
return parameterMapping.getValue()
.map(parametersNeedPurge)
.orElse(true); // Don't have the original values so purge
})
.map(Map.Entry::getKey) // The parameter id
.map(id->{
try {
return this.deleteByParametersId(layer.getName(), id);
} catch (StorageException e) {
throw new UncheckedIOException(e);
}
})
.reduce((x,y)->x||y) // OR results without short circuiting
.orElse(false);
} catch (UncheckedIOException ex) {
if(ex.getCause() instanceof StorageException) {
throw (StorageException) ex.getCause();
} else {
throw ex;
}
}
}
// /**
// * Test to see whether the blobstore is ready or not
// */
// public boolean isReady();
}