package org.geowebcache.diskquota.jdbc; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.Assert.assertThat; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Future; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.sql.DataSource; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.easymock.Capture; import org.easymock.classextension.EasyMock; import org.geowebcache.config.Configuration; import org.geowebcache.config.XMLConfiguration; import org.geowebcache.config.XMLConfigurationBackwardsCompatibilityTest; import org.geowebcache.diskquota.DiskQuotaMonitor; import org.geowebcache.diskquota.QuotaStore; import org.geowebcache.diskquota.storage.PageStats; import org.geowebcache.diskquota.storage.PageStatsPayload; import org.geowebcache.diskquota.storage.Quota; import org.geowebcache.diskquota.storage.StorageUnit; import org.geowebcache.diskquota.storage.SystemUtils; import org.geowebcache.diskquota.storage.TilePage; import org.geowebcache.diskquota.storage.TilePageCalculator; import org.geowebcache.diskquota.storage.TileSet; import org.geowebcache.diskquota.storage.TileSetVisitor; import org.geowebcache.filter.parameters.ParametersUtils; import org.geowebcache.grid.GridSetBroker; import org.geowebcache.layer.TileLayerDispatcher; import org.geowebcache.storage.DefaultStorageFinder; import org.geowebcache.storage.StorageBroker; import com.google.common.base.Objects; import org.hamcrest.Matchers; public abstract class JDBCQuotaStoreTest extends OnlineTestCase { JDBCQuotaStore store; File targetDir; DefaultStorageFinder cacheDirFinder; TileLayerDispatcher layerDispatcher; TilePageCalculator tilePageCalculator; private BasicDataSource dataSource; private TileSet testTileSet; private StorageBroker storageBroker; protected abstract SQLDialect getDialect(); protected BasicDataSource getDataSource() throws IOException, SQLException { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(fixture.getProperty("driver")); dataSource.setUrl(fixture.getProperty("url")); dataSource.setUsername(fixture.getProperty("username")); dataSource.setPassword(fixture.getProperty("password")); dataSource.setPoolPreparedStatements(true); dataSource.setAccessToUnderlyingConnectionAllowed(true); dataSource.setMinIdle(1); dataSource.setMaxActive(4); // if we cannot get a connection within 5 seconds give up dataSource.setMaxWait(5000); cleanupDatabase(dataSource); return dataSource; } protected void cleanupDatabase(DataSource dataSource) throws SQLException { // cleanup Connection cx = null; Statement st = null; try { cx = dataSource.getConnection(); st = cx.createStatement(); try { st.execute("DROP TABLE TILEPAGE CASCADE"); } catch(Exception e) { // fine } try { st.execute("DROP TABLE TILESET CASCADE"); } catch(Exception e) { // fine too } } finally { if(st != null) { st.close(); } if(cx != null) { cx.close(); } } } @Override protected void disconnect() throws Exception { store.close(); } @Override protected boolean isOnline() throws Exception { return true; } Map<String, Set<String>> parameterIdsMap; Map<String, Set<Map<String, String>>> parametersMap; private Collection<TileSet> expectedTileSets; private String[] paramIds; @Override protected void setUpInternal() throws Exception { // prepare a mock target directory for tiles targetDir = new File("target", "mockStore"); FileUtils.deleteDirectory(targetDir); targetDir.mkdirs(); cacheDirFinder = EasyMock.createMock(DefaultStorageFinder.class); EasyMock.expect(cacheDirFinder.getDefaultPath()).andReturn(targetDir.getAbsolutePath()) .anyTimes(); EasyMock.expect( cacheDirFinder.findEnvVar(EasyMock.eq(DiskQuotaMonitor.GWC_DISKQUOTA_DISABLED))) .andReturn(null).anyTimes(); EasyMock.replay(cacheDirFinder); XMLConfiguration xmlConfig = loadXMLConfig(); LinkedList<Configuration> configList = new LinkedList<Configuration>(); configList.add(xmlConfig); layerDispatcher = new TileLayerDispatcher(new GridSetBroker(true, true), configList); Capture<String> layerNameCap = new Capture<>(); storageBroker = EasyMock.createMock(StorageBroker.class); EasyMock.expect(storageBroker.getCachedParameterIds(EasyMock.capture(layerNameCap))) .andStubAnswer(()->parameterIdsMap.getOrDefault( layerNameCap.getValue(), Collections.singleton(null))); EasyMock.replay(storageBroker); parametersMap = new HashMap<>(); parametersMap.put("topp:states", Stream.of( "STYLE=&SOMEPARAMETER=", "STYLE=population&SOMEPARAMETER=2.0") .map(ParametersUtils::getMap) .collect(Collectors.toSet())); parameterIdsMap= parametersMap.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, e->e.getValue().stream() .map(ParametersUtils::getKvp) .collect(Collectors.toSet()) )); // add extra tests gwc configuration InputStream input = this.getClass().getClassLoader().getResourceAsStream("gwc-test-config.xml"); XMLConfiguration extraConfig = new XMLConfiguration(input); layerDispatcher.addConfiguration(extraConfig); tilePageCalculator = new TilePageCalculator(layerDispatcher, storageBroker); // prepare a connection pool for tests against a H2 database dataSource = getDataSource(); SQLDialect dialect = getDialect(); // setup the quota store store = new JDBCQuotaStore(cacheDirFinder, tilePageCalculator); store.setDataSource(dataSource); store.setDialect(dialect); // finally initialize the store store.initialize(); testTileSet = tilePageCalculator.getTileSetsFor("topp:states2").iterator().next(); paramIds = parameterIdsMap.get("topp:states").toArray(new String[2]); expectedTileSets = Arrays.asList( new TileSet("topp:states", "EPSG:900913", "image/png", paramIds[0]), new TileSet("topp:states", "EPSG:900913", "image/jpeg", paramIds[0]), new TileSet("topp:states", "EPSG:900913", "image/gif", paramIds[0]), new TileSet("topp:states", "EPSG:900913", "application/vnd.google-earth.kml+xml", paramIds[0]), new TileSet("topp:states", "EPSG:4326", "image/png", paramIds[0]), new TileSet("topp:states", "EPSG:4326", "image/jpeg", paramIds[0]), new TileSet("topp:states", "EPSG:4326", "image/gif", paramIds[0]), new TileSet("topp:states", "EPSG:4326", "application/vnd.google-earth.kml+xml", paramIds[0]), new TileSet("topp:states", "EPSG:900913", "image/png", paramIds[1]), new TileSet("topp:states", "EPSG:900913", "image/jpeg", paramIds[1]), new TileSet("topp:states", "EPSG:900913", "image/gif", paramIds[1]), new TileSet("topp:states", "EPSG:900913", "application/vnd.google-earth.kml+xml", paramIds[1]), new TileSet("topp:states", "EPSG:4326", "image/png", paramIds[1]), new TileSet("topp:states", "EPSG:4326", "image/jpeg", paramIds[1]), new TileSet("topp:states", "EPSG:4326", "image/gif", paramIds[1]), new TileSet("topp:states", "EPSG:4326", "application/vnd.google-earth.kml+xml", paramIds[1]), new TileSet("topp:states2", "EPSG:2163", "image/png", null), new TileSet("topp:states2", "EPSG:2163", "image/jpeg", null), new TileSet("topp:states3", "EPSG:4326", "image/png", null), new TileSet("topp:states3", "EPSG:2163", "image/png", null) ); } @Override protected void tearDownInternal() throws Exception { store.close(); } private XMLConfiguration loadXMLConfig() { InputStream is = null; XMLConfiguration xmlConfig = null; try { is = XMLConfiguration.class .getResourceAsStream(XMLConfigurationBackwardsCompatibilityTest.LATEST_FILENAME); xmlConfig = new XMLConfiguration(is); } catch (Exception e) { // Do nothing } finally { IOUtils.closeQuietly(is); } return xmlConfig; } public void testTableSetup() throws Exception { // on initialization we should have the tilesets setup properly // check the global quota Quota global = store.getGloballyUsedQuota(); assertNotNull(global); assertEquals(JDBCQuotaStore.GLOBAL_QUOTA_NAME, global.getTileSetId()); assertEquals(0, global.getBytes().longValue()); Set<TileSet> tileSets = store.getTileSets(); assertNotNull(tileSets); assertEquals(expectedTileSets.size(), tileSets.size()); for(TileSet tileSet : expectedTileSets) { assertTrue(tileSets.contains(tileSet)); assertQuotaZero(tileSet); } // check the layer wide quotas assertQuotaZero("topp:states"); assertQuotaZero("topp:states2"); assertQuotaZero("topp:states3"); // remove one layer from the dispatcher Configuration configuration = layerDispatcher.removeLayer("topp:states"); configuration.save(); // and make sure at the next startup the store catches up (note this behaviour is just a // startup consistency check in case the store got out of sync for some reason. On normal // situations the store should have been notified through store.deleteLayer(layerName) if // the layer was removed programmatically through StorageBroker.deleteLayer store.close(); store.setDataSource(getDataSource()); store.initialize(); tileSets = store.getTileSets(); assertNotNull(tileSets); assertEquals(4, tileSets.size()); TileSet tileSet = new TileSet("topp:states2", "EPSG:2163", "image/png", null); assertTrue(tileSets.contains(tileSet)); assertQuotaZero(tileSet); tileSet = new TileSet("topp:states2", "EPSG:2163", "image/jpeg", null); assertTrue(tileSets.contains(tileSet)); assertQuotaZero(tileSet); } public void testRenameLayer() throws InterruptedException { assertEquals(16, countTileSetsByLayerName("topp:states")); store.renameLayer("topp:states", "states_renamed"); assertEquals(0, countTileSetsByLayerName("topp:states")); assertEquals(16, countTileSetsByLayerName("states_renamed")); } public void testRenameLayer2() throws InterruptedException { final String oldLayerName = tilePageCalculator.getLayerNames().iterator().next(); final String newLayerName = "renamed_layer"; // make sure the layer is there and has stuff Quota usedQuota = store.getUsedQuotaByLayerName(oldLayerName); assertNotNull(usedQuota); TileSet tileSet = tilePageCalculator.getTileSetsFor(oldLayerName).iterator().next(); TilePage page = new TilePage(tileSet.getId(), 0, 0, (byte) 0); store.addHitsAndSetAccesTime(Collections.singleton(new PageStatsPayload(page))); store.addToQuotaAndTileCounts(tileSet, new Quota(BigInteger.valueOf(1024)), Collections.emptyList()); Quota expectedQuota = store.getUsedQuotaByLayerName(oldLayerName); assertEquals(1024L, expectedQuota.getBytes().longValue()); assertNotNull(store.getTileSetById(tileSet.getId())); store.renameLayer(oldLayerName, newLayerName); // cascade deleted old layer? assertNull(store.getLeastRecentlyUsedPage(Collections.singleton(oldLayerName))); usedQuota = store.getUsedQuotaByLayerName(oldLayerName); assertNotNull(usedQuota); assertEquals(0L, usedQuota.getBytes().longValue()); // created new layer? Quota newLayerUsedQuota = store.getUsedQuotaByLayerName(newLayerName); assertEquals(expectedQuota.getBytes(), newLayerUsedQuota.getBytes()); } public void testDeleteGridSet() throws InterruptedException { // put some data into four gridsets using two layers String layerName1 = "topp:states"; String layerName2 = "topp:states3"; TileSet tset1 = new TileSet(layerName1, "EPSG:4326", "image/jpeg", paramIds[0]); TileSet tset2 = new TileSet(layerName1, "EPSG:900913", "image/jpeg", paramIds[0]); TileSet tset3 = new TileSet(layerName2, "EPSG:4326", "image/png", null); TileSet tset4 = new TileSet(layerName1, "EPSG:4326", "image/png", paramIds[0]); addToQuotaStore(tset1); addToQuotaStore(tset2); addToQuotaStore(tset3); addToQuotaStore(tset4); // get the current quotas Quota tset1Quota = store.getUsedQuotaByTileSetId(tset1.getId()); Quota tset2Quota = store.getUsedQuotaByTileSetId(tset2.getId()); Quota tset3Quota = store.getUsedQuotaByTileSetId(tset3.getId()); Quota tset4Quota = store.getUsedQuotaByTileSetId(tset4.getId()); Quota globalQuota = store.getGloballyUsedQuota(); // check the current global quota Quota sum = new Quota(); sum.add(tset1Quota); sum.add(tset2Quota); sum.add(tset3Quota); sum.add(tset4Quota); assertEquals(globalQuota.getBytes(), sum.getBytes()); assertThat(store.getTileSets(), containsInAnyOrder( expectedTileSets.stream() .map(Matchers::equalTo) .collect(Collectors.toSet()))); store.deleteGridSubset(layerName1, "EPSG:4326"); assertThat(store.getTileSets(), containsInAnyOrder( expectedTileSets.stream() .filter(ts->!(ts.getGridsetId().equals("EPSG:4326") && ts.getLayerName().equals(layerName1))) .map(Matchers::equalTo) .collect(Collectors.toSet()))); // verify the quota for tset2 got erased and that now the total is equal to tset1 Quota newTset1Quota = store.getUsedQuotaByTileSetId(tset1.getId()); Quota newTset2Quota = store.getUsedQuotaByTileSetId(tset2.getId()); Quota newTset3Quota = store.getUsedQuotaByTileSetId(tset3.getId()); Quota newTset4Quota = store.getUsedQuotaByTileSetId(tset4.getId()); // validate test quota 1 assertNotNull(newTset1Quota); assertEquals(new BigInteger("0"), newTset1Quota.getBytes()); // validate test quota 2 assertNotNull(newTset2Quota); assertEquals(tset2Quota.getBytes(), newTset2Quota.getBytes()); // validate test quota 3 assertNotNull(newTset3Quota); assertEquals(tset3Quota.getBytes(), newTset3Quota.getBytes()); // validate test quota 4 assertNotNull(newTset4Quota); assertEquals(new BigInteger("0"), newTset4Quota.getBytes()); // test the global quota globalQuota = store.getGloballyUsedQuota(); assertEquals(tset2Quota.getBytes().add(tset3Quota.getBytes()), globalQuota.getBytes()); } public void testDeleteParameters() throws InterruptedException { // put some data into the two parameterizations String layerName = "topp:states"; TileSet tset1 = new TileSet(layerName, "EPSG:4326", "image/jpeg", paramIds[0]); addToQuotaStore(tset1); TileSet tset2 = new TileSet(layerName, "EPSG:4326", "image/jpeg", paramIds[1]); addToQuotaStore(tset2); Quota tset1Quota = store.getUsedQuotaByTileSetId(tset1.getId()); Quota tset2Quota = store.getUsedQuotaByTileSetId(tset2.getId()); Quota globalQuota = store.getGloballyUsedQuota(); Quota sum = new Quota(); sum.add(tset1Quota); sum.add(tset2Quota); assertEquals(globalQuota.getBytes(), sum.getBytes()); assertThat(store.getTileSets(), containsInAnyOrder( expectedTileSets.stream() .map(Matchers::equalTo) .collect(Collectors.toSet()))); store.deleteParameters("topp:states", paramIds[1]); assertThat(store.getTileSets(), containsInAnyOrder( expectedTileSets.stream() .filter(ts->!(Objects.equal(ts.getParametersId(), paramIds[1]) && ts.getLayerName().equals(layerName))) .map(Matchers::equalTo) .collect(Collectors.toSet()))); // verify the quota for tset2 got erased and that now the total is equal to tset1 tset1Quota = store.getUsedQuotaByTileSetId(tset1.getId()); tset2Quota = store.getUsedQuotaByTileSetId(tset2.getId()); assertNotNull(tset2Quota); assertEquals(new BigInteger("0"), tset2Quota.getBytes()); globalQuota = store.getGloballyUsedQuota(); assertEquals(tset1Quota.getBytes(), globalQuota.getBytes()); } private void addToQuotaStore(TileSet tset) throws InterruptedException { Quota quotaDiff = new Quota(5, StorageUnit.MiB); PageStatsPayload stats = new PageStatsPayload(new TilePage(tset.getId(), 0, 0, 3)); stats.setNumTiles(10); store.addToQuotaAndTileCounts(tset, quotaDiff, Collections.singletonList(stats)); } public void testDeleteLayer() throws InterruptedException { // put some data into the layer String layerName = "topp:states2"; TileSet tset = new TileSet(layerName, "EPSG:2163", "image/jpeg", null); addToQuotaStore(tset); // make sure the layer is there and has stuff Quota oldUsedQuota = store.getUsedQuotaByLayerName(layerName); assertNotNull(oldUsedQuota); Quota globalQuotaBefore = store.getGloballyUsedQuota(); assertTrue(oldUsedQuota.getBytes().longValue() > 0); assertTrue(globalQuotaBefore.getBytes().longValue() > 0); TileSet tileSet = tilePageCalculator.getTileSetsFor(layerName).iterator().next(); TilePage page = new TilePage(tileSet.getId(), 0, 0, (byte) 0); store.addHitsAndSetAccesTime(Collections.singleton(new PageStatsPayload(page))); assertNotNull(store.getTileSetById(tileSet.getId())); store.deleteLayer(layerName); // cascade deleted? assertNull(store.getLeastRecentlyUsedPage(Collections.singleton(layerName))); Quota usedQuota = store.getUsedQuotaByLayerName(layerName); assertNotNull(usedQuota); assertEquals(0L, usedQuota.getBytes().longValue()); // make sure the global quota got updated Quota globalQuotaAfter = store.getGloballyUsedQuota(); assertEquals(0, globalQuotaAfter.getBytes().longValue()); } public void testVisitor() throws Exception { Set<TileSet> tileSets1 = store.getTileSets(); final Set<TileSet> tileSets2 = new HashSet<TileSet>(); store.accept(new TileSetVisitor() { public void visit(TileSet tileSet, QuotaStore quotaStore) { tileSets2.add(tileSet); } }); assertEquals(tileSets1, tileSets2); } public void testGetTileSetById() throws Exception { TileSet tileSet = store.getTileSetById(testTileSet.getId()); assertNotNull(tileSet); assertEquals(testTileSet, tileSet); try { store.getTileSetById("NonExistentTileSetId"); fail("Expected IAE"); } catch (IllegalArgumentException e) { assertTrue(true); } } @SuppressWarnings("unchecked") public void testGetUsedQuotaByLayerName() throws Exception { String layerName = "topp:states2"; List<TileSet> tileSets; tileSets = new ArrayList<TileSet>(tilePageCalculator.getTileSetsFor(layerName)); Quota expected = new Quota(); for (TileSet tset : tileSets) { Quota quotaDiff = new Quota(10, StorageUnit.MiB); expected.add(quotaDiff); store.addToQuotaAndTileCounts(tset, quotaDiff, Collections.EMPTY_SET); } Quota usedQuotaByLayerName = store.getUsedQuotaByLayerName(layerName); assertEquals(expected.getBytes(), usedQuotaByLayerName.getBytes()); } @SuppressWarnings("unchecked") public void testGetUsedQuotaByTileSetId() throws Exception { String layerName = "topp:states2"; List<TileSet> tileSets; tileSets = new ArrayList<TileSet>(tilePageCalculator.getTileSetsFor(layerName)); Map<String, Quota> expectedById = new HashMap<String, Quota>(); for (TileSet tset : tileSets) { Quota quotaDiff = new Quota(10D * Math.random(), StorageUnit.MiB); store.addToQuotaAndTileCounts(tset, quotaDiff, Collections.EMPTY_SET); store.addToQuotaAndTileCounts(tset, quotaDiff, Collections.EMPTY_SET); Quota tsetQuota = new Quota(quotaDiff); tsetQuota.add(quotaDiff); expectedById.put(tset.getId(), tsetQuota); } for (Map.Entry<String, Quota> expected : expectedById.entrySet()) { BigInteger expectedValaue = expected.getValue().getBytes(); String tsetId = expected.getKey(); assertEquals(expectedValaue, store.getUsedQuotaByTileSetId(tsetId).getBytes()); } } public void testUpdateUsedQuotaWithParameters() throws Exception { // prepare a tileset with params String paramId = DigestUtils.sha1Hex("&styles=polygon"); TileSet tset = new TileSet("topp:states2", "EPSG:2163", "image/jpeg", paramId); Quota quotaDiff = new Quota(10D * Math.random(), StorageUnit.MiB); PageStatsPayload stats = new PageStatsPayload(new TilePage(tset.getId(), 0, 0, 3)); stats.setNumTiles(10); store.addToQuotaAndTileCounts(tset, quotaDiff, Collections.singletonList(stats)); assertEquals(quotaDiff.getBytes(), store.getUsedQuotaByTileSetId(tset.getId()).getBytes()); } /** * Combined test for {@link BDBQuotaStore#addToQuotaAndTileCounts(TileSet, Quota, Collection)} * and {@link BDBQuotaStore#addHitsAndSetAccesTime(Collection)} * * @throws Exception */ public void testPageStatsGathering() throws Exception { final MockSystemUtils sysUtils = new MockSystemUtils(); sysUtils.setCurrentTimeMinutes(10); sysUtils.setCurrentTimeMillis(10 * 60 * 1000); SystemUtils.set(sysUtils); TileSet tileSet = testTileSet; TilePage page = new TilePage(tileSet.getId(), 0, 0, (byte) 0); PageStatsPayload payload = new PageStatsPayload(page); int numHits = 100; payload.setTileSet(tileSet); payload.setLastAccessTime(sysUtils.currentTimeMillis() - 1 * 60 * 1000); payload.setNumHits(numHits); payload.setNumTiles(1); store.addToQuotaAndTileCounts(tileSet, new Quota(1, StorageUnit.MiB), Collections.singleton(payload)); Future<List<PageStats>> result = store.addHitsAndSetAccesTime(Collections .singleton(payload)); List<PageStats> allStats = result.get(); PageStats stats = allStats.get(0); float fillFactor = stats.getFillFactor(); assertEquals(1.0f, fillFactor, 1e-6); int lastAccessTimeMinutes = stats.getLastAccessTimeMinutes(); assertEquals(sysUtils.currentTimeMinutes(), lastAccessTimeMinutes); float frequencyOfUsePerMinute = stats.getFrequencyOfUsePerMinute(); assertEquals(100f, frequencyOfUsePerMinute); // now 1 minute later... sysUtils.setCurrentTimeMinutes(sysUtils.currentTimeMinutes() + 2); sysUtils.setCurrentTimeMillis(sysUtils.currentTimeMillis() + 2 * 60 * 1000); numHits = 10; payload.setLastAccessTime(sysUtils.currentTimeMillis() - 1 * 60 * 1000); payload.setNumHits(numHits); result = store.addHitsAndSetAccesTime(Collections.singleton(payload)); allStats = result.get(); stats = allStats.get(0); lastAccessTimeMinutes = stats.getLastAccessTimeMinutes(); assertEquals(11, lastAccessTimeMinutes); frequencyOfUsePerMinute = stats.getFrequencyOfUsePerMinute(); float expected = 55.0f;// the 100 previous + the 10 added now / the 2 minutes that elapsed assertEquals(expected, frequencyOfUsePerMinute, 1e-6f); } public void testGetGloballyUsedQuota() throws InterruptedException { Quota usedQuota = store.getGloballyUsedQuota(); assertNotNull(usedQuota); assertEquals(0, usedQuota.getBytes().intValue()); String layerName = tilePageCalculator.getLayerNames().iterator().next(); TileSet tileSet = tilePageCalculator.getTileSetsFor(layerName).iterator().next(); Quota quotaDiff = new Quota(BigInteger.valueOf(1000)); Collection<PageStatsPayload> tileCountDiffs = Collections.emptySet(); store.addToQuotaAndTileCounts(tileSet, quotaDiff, tileCountDiffs); usedQuota = store.getGloballyUsedQuota(); assertNotNull(usedQuota); assertEquals(1000, usedQuota.getBytes().intValue()); quotaDiff = new Quota(BigInteger.valueOf(-500)); store.addToQuotaAndTileCounts(tileSet, quotaDiff, tileCountDiffs); usedQuota = store.getGloballyUsedQuota(); assertNotNull(usedQuota); assertEquals(500, usedQuota.getBytes().intValue()); } public void testSetTruncated() throws Exception { String tileSetId = testTileSet.getId(); TilePage page = new TilePage(tileSetId, 0, 0, 2); PageStatsPayload payload = new PageStatsPayload(page); payload.setTileSet(testTileSet); int numHits = 100; payload.setNumHits(numHits); payload.setNumTiles(5); store.addToQuotaAndTileCounts(testTileSet, new Quota(1, StorageUnit.MiB), Collections.singleton(payload)); List<PageStats> stats = store.addHitsAndSetAccesTime(Collections.singleton(payload)).get(); assertTrue(stats.get(0).getFillFactor() > 0f); PageStats pageStats = store.setTruncated(page); assertEquals(0f, pageStats.getFillFactor()); } public void testGetLeastFrequentlyUsedPage() throws Exception { final String layerName = testTileSet.getLayerName(); Set<String> layerNames = Collections.singleton(layerName); TilePage lfuPage; lfuPage = store.getLeastFrequentlyUsedPage(layerNames); assertNull(lfuPage); TilePage page1 = new TilePage(testTileSet.getId(), 0, 1, 2); TilePage page2 = new TilePage(testTileSet.getId(), 1, 1, 2); PageStatsPayload payload1 = new PageStatsPayload(page1, testTileSet); PageStatsPayload payload2 = new PageStatsPayload(page2, testTileSet); payload1.setNumHits(100); payload2.setNumHits(10); Collection<PageStatsPayload> statsUpdates = Arrays.asList(payload1, payload2); store.addHitsAndSetAccesTime(statsUpdates).get(); TilePage leastFrequentlyUsedPage = store.getLeastFrequentlyUsedPage(layerNames); assertEquals(page2, leastFrequentlyUsedPage); payload2.setNumHits(1000); store.addHitsAndSetAccesTime(statsUpdates).get(); leastFrequentlyUsedPage = store.getLeastFrequentlyUsedPage(layerNames); assertEquals(page1, leastFrequentlyUsedPage); } public void testGetLeastFrequentlyUsedPageSkipEmpty() throws Exception { final String layerName = testTileSet.getLayerName(); Set<String> layerNames = Collections.singleton(layerName); TilePage lfuPage; lfuPage = store.getLeastFrequentlyUsedPage(layerNames); assertNull(lfuPage); TilePage page1 = new TilePage(testTileSet.getId(), 0, 1, 2); TilePage page2 = new TilePage(testTileSet.getId(), 1, 1, 2); PageStatsPayload payload1 = new PageStatsPayload(page1, testTileSet); PageStatsPayload payload2 = new PageStatsPayload(page2, testTileSet); payload1.setNumHits(100); payload2.setNumHits(10); Collection<PageStatsPayload> statsUpdates = Arrays.asList(payload1, payload2); store.addHitsAndSetAccesTime(statsUpdates).get(); TilePage leastFrequentlyUsedPage = store.getLeastFrequentlyUsedPage(layerNames); assertEquals(page2, leastFrequentlyUsedPage); store.setTruncated(page2); leastFrequentlyUsedPage = store.getLeastFrequentlyUsedPage(layerNames); assertEquals(page1, leastFrequentlyUsedPage); } public void testGetLeastRecentlyUsedPage() throws Exception { MockSystemUtils mockSystemUtils = new MockSystemUtils(); mockSystemUtils.setCurrentTimeMinutes(1000); mockSystemUtils.setCurrentTimeMillis(mockSystemUtils.currentTimeMinutes() * 60 * 1000); SystemUtils.set(mockSystemUtils); final String layerName = testTileSet.getLayerName(); Set<String> layerNames = Collections.singleton(layerName); TilePage leastRecentlyUsedPage; leastRecentlyUsedPage = store.getLeastRecentlyUsedPage(layerNames); assertNull(leastRecentlyUsedPage); TilePage page1 = new TilePage(testTileSet.getId(), 0, 1, 2); TilePage page2 = new TilePage(testTileSet.getId(), 1, 1, 2); PageStatsPayload payload1 = new PageStatsPayload(page1, testTileSet); PageStatsPayload payload2 = new PageStatsPayload(page2, testTileSet); payload1.setLastAccessTime(mockSystemUtils.currentTimeMillis() + 1 * 60 * 1000); payload2.setLastAccessTime(mockSystemUtils.currentTimeMillis() + 2 * 60 * 1000); Collection<PageStatsPayload> statsUpdates = Arrays.asList(payload1, payload2); store.addHitsAndSetAccesTime(statsUpdates).get(); leastRecentlyUsedPage = store.getLeastRecentlyUsedPage(layerNames); assertEquals(page1, leastRecentlyUsedPage); payload1.setLastAccessTime(mockSystemUtils.currentTimeMillis() + 10 * 60 * 1000); store.addHitsAndSetAccesTime(statsUpdates).get(); leastRecentlyUsedPage = store.getLeastRecentlyUsedPage(layerNames); assertEquals(page2, leastRecentlyUsedPage); } public void testGetLeastRecentlyUsedPageSkipEmpty() throws Exception { MockSystemUtils mockSystemUtils = new MockSystemUtils(); mockSystemUtils.setCurrentTimeMinutes(1000); mockSystemUtils.setCurrentTimeMillis(mockSystemUtils.currentTimeMinutes() * 60 * 1000); SystemUtils.set(mockSystemUtils); final String layerName = testTileSet.getLayerName(); Set<String> layerNames = Collections.singleton(layerName); TilePage leastRecentlyUsedPage; leastRecentlyUsedPage = store.getLeastRecentlyUsedPage(layerNames); assertNull(leastRecentlyUsedPage); TilePage page1 = new TilePage(testTileSet.getId(), 0, 1, 2); TilePage page2 = new TilePage(testTileSet.getId(), 1, 1, 2); PageStatsPayload payload1 = new PageStatsPayload(page1, testTileSet); PageStatsPayload payload2 = new PageStatsPayload(page2, testTileSet); payload1.setLastAccessTime(mockSystemUtils.currentTimeMillis() + 1 * 60 * 1000); payload2.setLastAccessTime(mockSystemUtils.currentTimeMillis() + 2 * 60 * 1000); Collection<PageStatsPayload> statsUpdates = Arrays.asList(payload1, payload2); store.addHitsAndSetAccesTime(statsUpdates).get(); leastRecentlyUsedPage = store.getLeastRecentlyUsedPage(layerNames); assertEquals(page1, leastRecentlyUsedPage); // truncate the page, setting its fill to 0 store.setTruncated(page1); leastRecentlyUsedPage = store.getLeastRecentlyUsedPage(layerNames); assertEquals(page2, leastRecentlyUsedPage); } public void testGetTilesForPage() throws Exception { TilePage page = new TilePage(testTileSet.getId(), 0, 0, 0); long[][] expected = tilePageCalculator.toGridCoverage(testTileSet, page); long[][] tilesForPage = store.getTilesForPage(page); assertTrue(Arrays.equals(expected[0], tilesForPage[0])); page = new TilePage(testTileSet.getId(), 0, 0, 1); expected = tilePageCalculator.toGridCoverage(testTileSet, page); tilesForPage = store.getTilesForPage(page); assertTrue(Arrays.equals(expected[1], tilesForPage[1])); } private int countTileSetsByLayerName(String layerName) { int count = 0; for (TileSet ts : store.getTileSets()) { if (layerName.equals(ts.getLayerName())) { count++; } } return count; } /** * Asserts the quota used by this tile set is null * * @param tileSet */ private void assertQuotaZero(TileSet tileSet) { Quota quota = store.getUsedQuotaByTileSetId(tileSet.getId()); assertNotNull(quota); assertEquals(0, quota.getBytes().longValue()); } /** * Asserts the quota used by this tile set is null * * @param tileSet * @throws InterruptedException */ private void assertQuotaZero(String layerName) throws InterruptedException { Quota quota = store.getUsedQuotaByLayerName(layerName); assertNotNull(quota); assertEquals(0, quota.getBytes().longValue()); } }