/**
* 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.
* <p>
* 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.
* <p>
* 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 Nuno Oliveira, GeoSolutions S.A.S., Copyright 2016
*/
package org.geowebcache.sqlite;
import org.geowebcache.io.ByteArrayResource;
import org.geowebcache.io.Resource;
import org.geowebcache.mime.MimeType;
import org.geowebcache.storage.TileObject;
import org.geowebcache.storage.TileRange;
import org.junit.Test;
import java.io.File;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.geowebcache.sqlite.Utils.Tuple.tuple;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
public final class MbtilesBlobStoreTest extends TestSupport {
@Test
public void testTilePutGetDeleteOperations() throws Exception {
// instantiating the store
MbtilesConfiguration configuration = getDefaultConfiguration();
MbtilesBlobStore store = new MbtilesBlobStore(configuration);
addStoresToClean(store);
// create the tile that will be stored
TileObject putTile = TileObject.createCompleteTileObject("africa",
new long[]{10, 50, 5}, "EPSG:4326", "image/png", null, stringToResource("IMAGE-10-50-5"));
// storing the tile
store.put(putTile);
// create a query tile to get the previous stored tile
TileObject getTile = TileObject.createQueryTileObject("africa",
new long[]{10, 50, 5}, "EPSG:4326", "image/png", null);
// checking if the tile was retrieved
assertThat(store.get(getTile), is(true));
// checking if the blob data was updated
assertThat(getTile.getBlob(), notNullValue());
assertThat(resourceToString(getTile.getBlob()), is("IMAGE-10-50-5"));
// sanity check of create time
assertThat(getTile.getCreated(), greaterThan(System.currentTimeMillis() - 60000));
// delete and check if the tile was deleted
assertThat(store.delete(putTile), is(true));
// query the deleted tile
getTile = TileObject.createQueryTileObject("africa",
new long[]{10, 50, 5}, "EPSG:4326", "image/png", null);
// check that the tile was deleted
assertThat(store.get(getTile), is(false));
assertThat(getTile.getBlob(), nullValue());
}
@Test
public void testTileMetadataOperations() throws Exception {
// instantiating the store
MbtilesConfiguration configuration = getDefaultConfiguration();
MbtilesBlobStore store = new MbtilesBlobStore(configuration);
addStoresToClean(store);
// let's store some metadata
store.putLayerMetadata("america", "background", "blue");
store.putLayerMetadata("america", "style", "america-style");
store.putLayerMetadata("australia", "background", "green");
store.putLayerMetadata("australia", "style", "australia-style");
// checking if the metadata was correctly stored
assertThat(store.getLayerMetadata("america", "background"), is("blue"));
assertThat(store.getLayerMetadata("america", "style"), is("america-style"));
assertThat(store.getLayerMetadata("australia", "background"), is("green"));
assertThat(store.getLayerMetadata("australia", "style"), is("australia-style"));
}
@Test
public void testTileMetadataOperationsWithNull() throws Exception {
// instantiating the store
MbtilesConfiguration configuration = getDefaultConfiguration();
MbtilesBlobStore store = new MbtilesBlobStore(configuration);
addStoresToClean(store);
// let's store some metadata with null values
store.putLayerMetadata(null, null, null);
store.putLayerMetadata("america", "style", null);
store.putLayerMetadata("australia", null, "green");
// checking if the metadata was correctly stored
assertThat(store.getLayerMetadata("america", "style"), nullValue());
assertThat(store.getLayerMetadata("australia", null), nullValue());
assertThat(store.getLayerMetadata(null, null), nullValue());
}
@Test
public void testDeleteLayerOperation() throws Exception {
// instantiating the store
MbtilesConfiguration configuration = getDefaultConfiguration();
MbtilesBlobStore store = new MbtilesBlobStore(configuration);
addStoresToClean(store);
// created database files for different layers
File asia1 = createFileInRootDir(Utils.buildPath("grid1", "asia", "image_png", "10", "tiles-0-500.sqlite"));
File asia2 = createFileInRootDir(Utils.buildPath("grid2", "asia", "image_png", "11", "tiles-100-500.sqlite"));
File asia3 = createFileInRootDir(Utils.buildPath("grid3", "asia", "image_png", "10", "tiles-0-500.sqlite"));
File asia4 = createFileInRootDir(Utils.buildPath("grid3", "asia", "image_jpeg", "10", "tiles-0-500.sqlite"));
File africa1 = createFileInRootDir(Utils.buildPath("grid1", "africa", "image_png", "10", "tiles-0-500.sqlite"));
File america1 = createFileInRootDir(Utils.buildPath("grid1", "america", "image_png", "10", "tiles-0-500.sqlite"));
File america2 = createFileInRootDir(Utils.buildPath("grid2", "america", "image_jpeg", "10", "tiles-0-500.sqlite"));
File europe1 = createFileInRootDir(Utils.buildPath("grid1", "europe", "image_gif", "15", "tiles-4000-500.sqlite"));
// deleting layer asia and europe
store.delete("asia");
store.delete("europe");
assertThat(asia1.exists(), is(false));
assertThat(asia2.exists(), is(false));
assertThat(asia3.exists(), is(false));
assertThat(asia4.exists(), is(false));
assertThat(africa1.exists(), is(true));
assertThat(america1.exists(), is(true));
assertThat(america2.exists(), is(true));
assertThat(europe1.exists(), is(false));
}
@Test
public void testDeleteLayerWithGridSetOperation() throws Exception {
// instantiating the store
MbtilesConfiguration configuration = getDefaultConfiguration();
MbtilesBlobStore store = new MbtilesBlobStore(configuration);
addStoresToClean(store);
// created database files for different layers
File asia1 = createFileInRootDir(Utils.buildPath("grid1", "asia", "image_png", "10", "tiles-0-500.sqlite"));
File asia2 = createFileInRootDir(Utils.buildPath("grid2", "asia", "image_png", "11", "tiles-100-500.sqlite"));
File asia3 = createFileInRootDir(Utils.buildPath("grid3", "asia", "image_png", "10", "tiles-0-500.sqlite"));
File asia4 = createFileInRootDir(Utils.buildPath("grid3", "asia", "image_jpeg", "10", "tiles-0-500.sqlite"));
File africa1 = createFileInRootDir(Utils.buildPath("grid1", "africa", "image_png", "10", "tiles-0-500.sqlite"));
File america1 = createFileInRootDir(Utils.buildPath("grid1", "america", "image_png", "10", "tiles-0-500.sqlite"));
File america2 = createFileInRootDir(Utils.buildPath("grid2", "america", "image_jpeg", "10", "tiles-0-500.sqlite"));
File europe1 = createFileInRootDir(Utils.buildPath("grid1", "europe", "image_gif", "15", "tiles-4000-500.sqlite"));
// deleting layer asia grid set grid3 and america grid set grid2
store.deleteByGridsetId("asia", "grid3");
store.deleteByGridsetId("america", "grid2");
assertThat(asia1.exists(), is(true));
assertThat(asia2.exists(), is(true));
assertThat(asia3.exists(), is(false));
assertThat(asia4.exists(), is(false));
assertThat(africa1.exists(), is(true));
assertThat(america1.exists(), is(true));
assertThat(america2.exists(), is(false));
assertThat(europe1.exists(), is(true));
}
@Test
public void testDeleteLayerWithTileRangeEager() throws Exception {
// instantiating the store
MbtilesConfiguration configuration = getDefaultConfiguration();
// activate eager mode
configuration.setEagerDelete(true);
MbtilesBlobStore store = new MbtilesBlobStore(configuration);
addStoresToClean(store);
// create the tile range
long[][] rangeBounds = new long[][]{
{0, 490, 10, 500, 10},
{800, 950, 1005, 1020, 11}
};
TileRange tileRange = new TileRange("asia", "grid1", 10, 11, rangeBounds,
MimeType.createFromExtension("png"), Collections.emptyMap());
// created layer database files
File asia1 = createFileInRootDir(Utils.buildPath("grid1", "asia", "image_png", "10", "tiles-0-0.sqlite"));
File asia2 = createFileInRootDir(Utils.buildPath("grid1", "asia", "image_png", "10", "tiles-0-500.sqlite"));
File asia3 = createFileInRootDir(Utils.buildPath("grid1", "asia", "image_png", "10", "tiles-500-0.sqlite"));
File asia4 = createFileInRootDir(Utils.buildPath("grid1", "asia", "image_png", "10", "tiles-500-500.sqlite"));
File asia5 = createFileInRootDir(Utils.buildPath("grid1", "asia", "image_png", "11", "tiles-1000-1000.sqlite"));
// deleting tiles range
store.delete(tileRange);
assertThat(asia1.exists(), is(false));
assertThat(asia2.exists(), is(false));
assertThat(asia3.exists(), is(true));
assertThat(asia4.exists(), is(true));
assertThat(asia5.exists(), is(false));
}
@Test
public void testDeleteLayerWithTileRangeNoEager() throws Exception {
// instantiating the store
MbtilesConfiguration configuration = getDefaultConfiguration();
MbtilesBlobStore store = new MbtilesBlobStore(configuration);
addStoresToClean(store);
// create the tile range
long[][] rangeBounds = new long[][]{
{0, 10, 5, 15, 10},
{980, 950, 1005, 1020, 11}
};
TileRange tileRange = new TileRange("asia", "grid1", 10, 11, rangeBounds,
MimeType.createFromExtension("png"), Collections.emptyMap());
// created layer database files
File asia1 = buildRootFile(Utils.buildPath("grid1", "asia", "image_png", "10", "tiles-0-0.sqlite"));
File asia2 = buildRootFile(Utils.buildPath("grid1", "asia", "image_png", "10", "tiles-500-500.sqlite"));
// create some tiles
store.put(TileObject.createCompleteTileObject("asia",
new long[]{3, 12, 10}, "grid1", "image/png", null, stringToResource("IMAGE-10-50-10")));
store.put(TileObject.createCompleteTileObject("asia",
new long[]{510, 550, 10}, "grid1", "image/png", null, stringToResource("IMAGE-510-550-10")));
// deleting tiles range
store.delete(tileRange);
assertThat(asia1.exists(), is(true));
assertThat(asia2.exists(), is(true));
// check that the correct tiles were deleted
assertThat(store.get(TileObject.createQueryTileObject("asia",
new long[]{10, 50, 10}, "grid1", "image/png", null)), is(false));
assertThat(store.get(TileObject.createQueryTileObject("asia",
new long[]{510, 550, 10}, "grid1", "image/png", null)), is(true));
}
@Test
public void testLayerExistsOperation() throws Exception {
// instantiating the store
MbtilesConfiguration configuration = getDefaultConfiguration();
MbtilesBlobStore store = new MbtilesBlobStore(configuration);
addStoresToClean(store);
// created layer database file
createFileInRootDir(Utils.buildPath("grid1", "europe", "image_png", "10", "tiles-0-0.sqlite"));
// checking if layer exists
assertThat(store.layerExists("europe"), is(true));
assertThat(store.layerExists("asia"), is(false));
}
@Test
public void testRenameOperation() throws Exception {
// instantiating the store
MbtilesConfiguration configuration = getDefaultConfiguration();
MbtilesBlobStore store = new MbtilesBlobStore(configuration);
addStoresToClean(store);
// create layers database files
File asia1 = createFileInRootDir(Utils.buildPath("grid1", "asia", "image_png", "10", "tiles-0-0.sqlite"));
File asia2 = createFileInRootDir(Utils.buildPath("grid1", "asia", "image_png", "10", "tiles-0-500.sqlite"));
File europe1 = createFileInRootDir(Utils.buildPath("grid1", "europe", "image_png", "10", "tiles-500-0.sqlite"));
// rename asia layers
store.rename("asia", "australia");
assertThat(asia1.exists(), is(false));
assertThat(asia2.exists(), is(false));
assertThat(europe1.exists(), is(true));
assertThat(buildRootFile("grid1", "australia", "image_png", "10", "tiles-0-0.sqlite").exists(), is(true));
assertThat(buildRootFile("grid1", "australia", "image_png", "10", "tiles-0-500.sqlite").exists(), is(true));
}
@Test
public void testOpeningDatabaseFileWithMbtilesMetadata() throws Exception {
// create and instantiate mbtiles metadata
File mbtilesMetadataDirectory = buildRootFile("mbtiles-metadata");
File mbtilesMetadataFile = new File(mbtilesMetadataDirectory, "europe_asia.properties");
String mbtilesMetadata = "attribution=some attribution" + System.lineSeparator() +
"bounds=-180,-90,180,90" + System.lineSeparator() +
"description=some description" + System.lineSeparator() +
"maxZoom=10" + System.lineSeparator() +
"minZoom=0" + System.lineSeparator() +
"type=base_layer" + System.lineSeparator() +
"version=1.0" + System.lineSeparator();
writeToFile(mbtilesMetadataFile, mbtilesMetadata);
// instantiating the store
MbtilesConfiguration configuration = getDefaultConfiguration();
configuration.setMbtilesMetadataDirectory(mbtilesMetadataDirectory.getPath());
SqliteConnectionManager connectionManager = new SqliteConnectionManager(configuration);
MbtilesBlobStore store = new MbtilesBlobStore(configuration, connectionManager);
addStoresToClean(store);
// create the tile that will be stored
TileObject putTile = TileObject.createCompleteTileObject("europe:asia",
new long[]{5, 5, 5}, "EPSG:4326", "image/png", null, stringToResource("IMAGE-5-5-5"));
// storing the tile
store.put(putTile);
// checking if the file exists
File file = buildRootFile("EPSG_4326", "europe_asia", "image_png", "5", "tiles-0-0.sqlite");
assertThat(file.exists(), is(true));
// checking that the user provided metadata was properly inserted
connectionManager.executeQuery(file, resultSet -> {
// extract the metadata values from the result set
List<Utils.Tuple<String, String>> foundMetadata = new ArrayList<>();
while(resultSet.next()) {
foundMetadata.add(tuple(resultSet.getString(1), resultSet.getString(2)));
}
// let's see if we have the expected metadata
assertThat(foundMetadata.size(), is(9));
// the provided minZoom and maxZoom are ignored (geotools computes the real values)
assertThat(foundMetadata, containsInAnyOrder(tuple("name", "europe:asia"),
tuple("version", "1.0"),
tuple("description", "some description"),
tuple("attribution", "some attribution"),
tuple("type", "base_layer"),
tuple("format", "png"),
tuple("bounds", "-180.0,-90.0,180.0,90.0"),
tuple("minzoom", "5"),
tuple("maxzoom", "5")));
return null;
}, "SELECT name, value FROM metadata;");
}
}