Ticket #11216: jcs-cache-v1.patch

File jcs-cache-v1.patch, 27.3 KB (added by wiktorn, 4 years ago)
  • build.xml

     
    220220            destdir="build" target="1.7" source="1.7" debug="on" includeantruntime="false" createMissingPackageInfoClass="false" encoding="iso-8859-1">
    221221            <!-- get rid of "internal proprietary API" warning -->
    222222            <compilerarg value="-XDignore.symbol.file"/>
     223                <exclude name="org/apache/commons/jcs/admin/**"/>
     224                <exclude name="org/apache/commons/jcs/auxiliary/disk/jdbc/**"/>
     225                <exclude name="org/apache/commons/jcs/auxiliary/remote/**"/>
     226                <exclude name="org/apache/commons/jcs/utils/servlet/**"/>
     227                <exclude name="org/apache/commons/logging/impl/AvalonLogger.java"/>
     228                <exclude name="org/apache/commons/logging/impl/Log4JLogger.java"/>
     229                <exclude name="org/apache/commons/logging/impl/LogKitLogger.java"/>
     230                <exclude name="org/apache/commons/logging/impl/ServletContextCleaner.java"/>
    223231        </javac>
    224232        <!-- JMapViewer/JOSM -->
    225233        <javac srcdir="${src.dir}" excludes="com/**,oauth/**,org/apache/commons/**,org/glassfish/**,org/openstreetmap/gui/jmapviewer/Demo.java"
     
    581589        </java>
    582590    </target>
    583591</project>
     592
  • src/org/apache/commons

  • src/org/openstreetmap/gui/jmapviewer/OsmFileCacheTileLoader.java

    Property changes on: src/org/apache/commons
    ___________________________________________________________________
    Modified: svn:externals
    ## -1 +1,3 ##
    -codec http://svn.apache.org/repos/asf/commons/proper/codec/trunk/src/main/java/org/apache/commons/codec
    +http://svn.apache.org/repos/asf/commons/proper/codec/trunk/src/main/java/org/apache/commons/codec codec
    +http://svn.apache.org/repos/asf/commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs jcs
    +http://svn.apache.org/repos/asf/commons/proper/logging/trunk/src/main/java/org/apache/commons/logging logging
     
    11// License: GPL. For details, see Readme.txt file.
    22package org.openstreetmap.gui.jmapviewer;
    33
    4 import java.io.BufferedReader;
    54import java.io.ByteArrayInputStream;
    65import java.io.ByteArrayOutputStream;
    76import java.io.File;
    8 import java.io.FileInputStream;
    9 import java.io.FileNotFoundException;
    10 import java.io.FileOutputStream;
    117import java.io.IOException;
    128import java.io.InputStream;
    13 import java.io.InputStreamReader;
    14 import java.io.OutputStreamWriter;
    15 import java.io.PrintWriter;
    169import java.net.HttpURLConnection;
    1710import java.net.URL;
    1811import java.net.URLConnection;
    1912import java.nio.charset.Charset;
    20 import java.util.HashMap;
    21 import java.util.Map;
    22 import java.util.Map.Entry;
    2313import java.util.Random;
    2414import java.util.logging.Level;
    2515import java.util.logging.Logger;
    2616
     17import org.apache.commons.jcs.access.behavior.ICacheAccess;
     18import org.apache.commons.jcs.engine.behavior.ICacheElement;
     19import org.apache.commons.jcs.engine.behavior.IElementAttributes;
    2720import org.openstreetmap.gui.jmapviewer.interfaces.CachedTileLoader;
    2821import org.openstreetmap.gui.jmapviewer.interfaces.TileClearController;
    2922import org.openstreetmap.gui.jmapviewer.interfaces.TileJob;
     
    3124import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
    3225import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
    3326import org.openstreetmap.gui.jmapviewer.interfaces.TileSource.TileUpdate;
     27import org.openstreetmap.josm.data.imagery.cache.CacheEntry;
     28import org.openstreetmap.josm.data.imagery.cache.CacheEntryAttributes;
     29import org.openstreetmap.josm.data.imagery.cache.TileCacheManager;
    3430
    3531/**
    3632 * A {@link TileLoader} implementation that loads tiles from OSM via HTTP and
     
    5753    // even if the refresh from the server fails.
    5854    protected static final long ABSOLUTE_EXPIRE_TIME_LIMIT = Long.MAX_VALUE; // unlimited
    5955
    60     protected String cacheDirBase;
    61 
    62     protected final Map<TileSource, File> sourceCacheDirMap;
     56    protected ICacheAccess<String, CacheEntry> cache;
    6357
    6458
    6559    public static File getDefaultCacheDir() throws SecurityException {
     
    7064        } catch (SecurityException e) {
    7165            log.log(Level.WARNING,
    7266                    "Failed to access system property ''java.io.tmpdir'' for security reasons. Exception was: "
    73                         + e.toString());
     67                            + e.toString());
    7468            throw e; // rethrow
    7569        }
    7670        try {
     
    9791     */
    9892    public OsmFileCacheTileLoader(TileLoaderListener map, File cacheDir) throws IOException  {
    9993        super(map);
    100         if (cacheDir == null || (!cacheDir.exists() && !cacheDir.mkdirs()))
    101             throw new IOException("Cannot access cache directory");
    102 
    103         log.finest("Tile cache directory: " + cacheDir);
    104         cacheDirBase = cacheDir.getAbsolutePath();
    105         sourceCacheDirMap = new HashMap<>();
     94        cache = TileCacheManager.getCache("TMS");
    10695    }
    10796
    10897    /**
     
    119108        return new FileLoadJob(tile);
    120109    }
    121110
    122     protected File getSourceCacheDir(TileSource source) {
    123         File dir = sourceCacheDirMap.get(source);
    124         if (dir == null) {
    125             dir = new File(cacheDirBase, source.getName().replaceAll("[\\\\/:*?\"<>|]", "_"));
    126             if (!dir.exists()) {
    127                 dir.mkdirs();
    128             }
    129         }
    130         return dir;
    131     }
    132 
    133111    protected class FileLoadJob implements TileJob {
    134112        InputStream input = null;
    135113
    136114        Tile tile;
    137         File tileCacheDir;
    138         File tileFile = null;
    139         File tagsFile = null;
    140         Long fileMtime = null;
     115        ICacheElement<String, CacheEntry> element;
     116        CacheEntryAttributes elementAttributes;
    141117        Long now = null; // current time in milliseconds (keep consistent value for the whole run)
    142118
    143119        public FileLoadJob(Tile tile) {
     
    159135                tile.loading = true;
    160136            }
    161137            now = System.currentTimeMillis();
    162             tileCacheDir = getSourceCacheDir(tile.getSource());
    163             tileFile = getTileFile();
    164             tagsFile = getTagsFile();
    165138
    166             loadTagsFromFile();
     139            element = cache.getCacheElement(tile.getKey());
     140            elementAttributes = getCacheEntryAttributes(element);
    167141
    168142            if (isCacheValid() && (isNoTileAtZoom() || loadTileFromFile())) {
    169143                log.log(Level.FINE, "TMS - found in tile cache: {0}", tile);
     
    204178        protected boolean loadOrUpdateTile() {
    205179            try {
    206180                URLConnection urlConn = loadTileFromOsm(tile);
    207                 if (fileMtime != null && now - fileMtime <= ABSOLUTE_EXPIRE_TIME_LIMIT) {
     181
     182                if (now - elementAttributes.getLastModification() <= ABSOLUTE_EXPIRE_TIME_LIMIT) {
    208183                    switch (tile.getSource().getTileUpdate()) {
    209184                    case IfModifiedSince:
    210                         urlConn.setIfModifiedSince(fileMtime);
     185                        urlConn.setIfModifiedSince(elementAttributes.getLastModification());
    211186                        break;
    212187                    case LastModified:
    213                         if (!isOsmTileNewer(fileMtime)) {
     188                        if (!isOsmTileNewer(elementAttributes.getLastModification())) {
    214189                            log.log(Level.FINE, "TMS - LastModified test: local version is up to date: {0}", tile);
    215                             tileFile.setLastModified(now);
     190                            elementAttributes.setLastModification(now);
    216191                            return true;
    217192                        }
    218193                        break;
     
    221196                    }
    222197                }
    223198                if (tile.getSource().getTileUpdate() == TileUpdate.ETag || tile.getSource().getTileUpdate() == TileUpdate.IfNoneMatch) {
    224                     String fileETag = tile.getValue("etag");
     199                    String fileETag = elementAttributes.getEtag();
    225200                    if (fileETag != null) {
    226201                        switch (tile.getSource().getTileUpdate()) {
    227202                        case IfNoneMatch:
     
    230205                        case ETag:
    231206                            if (hasOsmTileETag(fileETag)) {
    232207                                log.log(Level.FINE, "TMS - ETag test: local version is up to date: {0}", tile);
    233                                 tileFile.setLastModified(now);
     208                                elementAttributes.setLastModification(now);
    234209                                return true;
    235210                            }
    236211                        default:
    237212                            break;
    238213                        }
    239214                    }
    240                     tile.putValue("etag", urlConn.getHeaderField("ETag"));
     215                    elementAttributes.setEtag(urlConn.getHeaderField("ETag"));
    241216                }
    242217                if (urlConn instanceof HttpURLConnection && ((HttpURLConnection)urlConn).getResponseCode() == 304) {
    243218                    // If isModifiedSince or If-None-Match has been set
     
    253228                        break;
    254229                    }
    255230                    loadTileFromFile();
    256                     tileFile.setLastModified(now);
     231                    elementAttributes.setLastModification(now);
    257232                    return true;
    258233                }
    259234
    260235                loadTileMetadata(tile, urlConn);
    261                 saveTagsToFile();
    262236
    263                 if ("no-tile".equals(tile.getValue("tile-info")))
     237                if (elementAttributes.isNoTileAtZoom())
    264238                {
    265239                    log.log(Level.FINE, "TMS - No tile: tile-info=no-tile: {0}", tile);
    266240                    tile.setError("No tile at this zoom level");
     241                    cache.put(tile.getKey(), new CacheEntry(new byte[]{}), elementAttributes);
    267242                    return true;
    268243                } else {
    269244                    for (int i = 0; i < 5; ++i) {
     
    274249                        byte[] buffer = loadTileInBuffer(urlConn);
    275250                        if (buffer != null) {
    276251                            tile.loadImage(new ByteArrayInputStream(buffer));
    277                             saveTileToFile(buffer);
     252                            cache.put(tile.getKey(), new CacheEntry(buffer), elementAttributes);
    278253                            log.log(Level.FINE, "TMS - downloaded tile from server: {0}", tile.getUrl());
    279254                            return true;
    280255                        }
     
    294269            return false;
    295270        }
    296271
     272
     273        private void loadTileMetadata(Tile tile, URLConnection urlConn) {
     274            elementAttributes.setNoTileAtZoom("no-tile".equals(urlConn.getHeaderField("X-VE-Tile-Info")));
     275
     276            Long lng = urlConn.getExpiration();
     277            if (lng.equals(0L)) {
     278                try {
     279                    String str = urlConn.getHeaderField("Cache-Control");
     280                    if (str != null) {
     281                        for (String token: str.split(",")) {
     282                            if (token.startsWith("max-age=")) {
     283                                lng = Long.parseLong(token.substring(8)) * 1000 +
     284                                        System.currentTimeMillis();
     285                            }
     286                        }
     287                    }
     288                } catch (NumberFormatException e) {} //ignore malformed Cache-Control headers
     289            }
     290
     291            elementAttributes.setExpirationTime(lng);
     292        }
     293
     294        private final CacheEntryAttributes getCacheEntryAttributes(ICacheElement e) {
     295            if (e != null) {
     296                IElementAttributes ea = e.getElementAttributes();
     297                return (CacheEntryAttributes) ea;
     298            } else {
     299                CacheEntryAttributes ret =  new CacheEntryAttributes();
     300                ret.setLastModification(now);
     301                ret.setExpirationTime(now + DEFAULT_EXPIRE_TIME);
     302                return ret;
     303            }
     304        }
     305
    297306        protected boolean isCacheValid() {
    298             Long expires = null;
    299             if (tileFile.exists()) {
    300                 fileMtime = tileFile.lastModified();
    301             } else if (tagsFile.exists()) {
    302                 fileMtime = tagsFile.lastModified();
    303             } else
     307            if (element == null)
    304308                return false;
    305309
    306             try {
    307                 expires = Long.parseLong(tile.getValue("expires"));
    308             } catch (NumberFormatException e) {}
     310            long expires = elementAttributes.getExpirationTime();
    309311
    310312            // check by expire date set by server
    311             if (expires != null && !expires.equals(0L)) {
     313            if (expires != 0L) {
    312314                // put a limit to the expire time (some servers send a value
    313315                // that is too large)
    314                 expires = Math.min(expires, fileMtime + EXPIRE_TIME_SERVER_LIMIT);
     316                expires = Math.min(expires, elementAttributes.getCreateTime() + EXPIRE_TIME_SERVER_LIMIT);
    315317                if (now > expires) {
    316                     log.log(Level.FINE, "TMS - Tile has expired -> not valid {0}", tile);
     318                    log.log(Level.FINE, "TMS - Tile has expired ({0})-> not valid {1}", new Object[]{Long.toString(expires), tile});
    317319                    return false;
    318320                }
    319321            } else {
    320322                // check by file modification date
    321                 if (now - fileMtime > DEFAULT_EXPIRE_TIME) {
     323                if (now - elementAttributes.getLastModification() > DEFAULT_EXPIRE_TIME) {
    322324                    log.log(Level.FINE, "TMS - Tile has expired, maximum file age reached {0}", tile);
    323325                    return false;
    324326                }
     
    327329        }
    328330
    329331        protected boolean isNoTileAtZoom() {
    330             if ("no-tile".equals(tile.getValue("tile-info"))) {
     332            if (elementAttributes.isNoTileAtZoom()) {
    331333                // do not remove file - keep the information, that there is no tile, for further requests
    332334                // the code above will check, if this information is still valid
    333335                log.log(Level.FINE, "TMS - Tile valid, but no file, as no tiles at this level {0}", tile);
     
    338340        }
    339341
    340342        protected boolean loadTileFromFile() {
    341             if (!tileFile.exists())
    342                 return false;
    343 
    344             try (FileInputStream fin = new FileInputStream(tileFile)) {
     343            try (InputStream fin = new ByteArrayInputStream(element.getVal().getContent())) {
    345344                if (fin.available() == 0)
    346345                    throw new IOException("File empty");
    347346                tile.loadImage(fin);
    348347                return true;
    349348            } catch (Exception e) {
    350349                log.log(Level.WARNING, "TMS - Error while loading image from tile cache: {0}; {1}", new Object[]{e.getMessage(), tile});
    351                 tileFile.delete();
    352                 if (tagsFile.exists()) {
    353                     tagsFile.delete();
    354                 }
    355                 tileFile = null;
    356                 fileMtime = null;
    357350            }
    358351            return false;
    359352        }
     
    428421                return true;
    429422            return (osmETag.equals(eTag));
    430423        }
    431 
    432         protected File getTileFile() {
    433             return new File(tileCacheDir + "/" + tile.getZoom() + "_" + tile.getXtile() + "_" + tile.getYtile() + "."
    434                     + tile.getSource().getTileType());
    435         }
    436 
    437         protected File getTagsFile() {
    438             return new File(tileCacheDir + "/" + tile.getZoom() + "_" + tile.getXtile() + "_" + tile.getYtile() + "."
    439                     + TAGS_FILE_EXT);
    440         }
    441 
    442         protected void saveTileToFile(byte[] rawData) {
    443             File file = getTileFile();
    444             file.getParentFile().mkdirs();
    445             try (FileOutputStream f = new FileOutputStream(file)) {
    446                 f.write(rawData);
    447             } catch (Exception e) {
    448                 log.log(Level.SEVERE, "Failed to save tile content: {0}", e.getLocalizedMessage());
    449             }
    450         }
    451 
    452         protected void saveTagsToFile() {
    453             File tagsFile = getTagsFile();
    454             tagsFile.getParentFile().mkdirs();
    455             if (tile.getMetadata() == null) {
    456                 tagsFile.delete();
    457                 return;
    458             }
    459             try (PrintWriter f = new PrintWriter(new OutputStreamWriter(new FileOutputStream(tagsFile), TAGS_CHARSET))) {
    460                 for (Entry<String, String> entry : tile.getMetadata().entrySet()) {
    461                     f.println(entry.getKey() + "=" + entry.getValue());
    462                 }
    463             } catch (Exception e) {
    464                 System.err.println("Failed to save tile tags: " + e.getLocalizedMessage());
    465             }
    466         }
    467 
    468         protected boolean loadTagsFromFile() {
    469             File tagsFile = getTagsFile();
    470             try (BufferedReader f = new BufferedReader(new InputStreamReader(new FileInputStream(tagsFile), TAGS_CHARSET))) {
    471                 for (String line = f.readLine(); line != null; line = f.readLine()) {
    472                     final int i = line.indexOf('=');
    473                     if (i == -1 || i == 0) {
    474                         System.err.println("Malformed tile tag in file '" + tagsFile.getName() + "':" + line);
    475                         continue;
    476                     }
    477                     tile.putValue(line.substring(0,i),line.substring(i+1));
    478                 }
    479             } catch (FileNotFoundException e) {
    480             } catch (Exception e) {
    481                 System.err.println("Failed to load tile tags: " + e.getLocalizedMessage());
    482             }
    483 
    484             return true;
    485         }
    486424    }
    487425
    488     public String getCacheDirBase() {
    489         return cacheDirBase;
    490     }
    491426
    492     public void setTileCacheDir(String tileCacheDir) {
    493         File dir = new File(tileCacheDir);
    494         dir.mkdirs();
    495         this.cacheDirBase = dir.getAbsolutePath();
    496     }
    497427
    498428    @Override
    499429    public void clearCache(TileSource source) {
     
    502432
    503433    @Override
    504434    public void clearCache(TileSource source, TileClearController controller) {
    505         File dir = getSourceCacheDir(source);
    506         if (dir != null) {
    507             if (controller != null) controller.initClearDir(dir);
    508             if (dir.isDirectory()) {
    509                 File[] files = dir.listFiles();
    510                 if (controller != null) controller.initClearFiles(files);
    511                 for (File file : files) {
    512                     if (controller != null && controller.cancel()) return;
    513                     file.delete();
    514                     if (controller != null) controller.fileDeleted(file);
    515                 }
    516             }
    517             dir.delete();
    518         }
     435        cache.clear();
    519436        if (controller != null) controller.clearFinished();
    520437    }
    521438}
  • src/org/openstreetmap/gui/jmapviewer/TMSFileCacheTileLoader.java

     
    33
    44import java.io.File;
    55import java.io.IOException;
     6
    67import org.openstreetmap.gui.jmapviewer.interfaces.TileJob;
    78import org.openstreetmap.gui.jmapviewer.interfaces.TileLoaderListener;
    8 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
    99
    1010/**
    1111 * Reworked version of the OsmFileCacheTileLoader.
     
    3030            super(tile);
    3131        }
    3232
    33         @Override
    34         protected File getTileFile() {
    35             return getDataFile(tile.getSource().getTileType());
    36         }
    37 
    38         @Override
    39         protected File getTagsFile() {
    40             return getDataFile(TAGS_FILE_EXT);
    41         }
    42 
    43         protected File getDataFile(String ext) {
    44             int nDigits = (int) Math.ceil(Math.log10(1 << tile.getZoom()));
    45             String x = String.format("%0" + nDigits + "d", tile.getXtile());
    46             String y = String.format("%0" + nDigits + "d", tile.getYtile());
    47             File path = new File(tileCacheDir, "z" + tile.getZoom());
    48             for (int i=0; i<nDigits; i++) {
    49                 String component = "x" + x.substring(i, i+1) + "y" + y.substring(i, i+1);
    50                 if (i == nDigits -1 ) {
    51                     component += "." + ext;
    52                 }
    53                 path = new File(path, component);
    54             }
    55             return path;
    56         }
    57     }
    5833
    59     @Override
    60     protected File getSourceCacheDir(TileSource source) {
    61         File dir = sourceCacheDirMap.get(source);
    62         if (dir == null) {
    63             String id = source.getId();
    64             if (id != null) {
    65                 dir = new File(cacheDirBase, id);
    66             } else {
    67                 dir = new File(cacheDirBase, source.getName().replaceAll("[\\\\/:*?\"<>|]", "_"));
    68             }
    69             if (!dir.exists()) {
    70                 dir.mkdirs();
    71             }
    72         }
    73         return dir;
    7434    }
    75 
    7635}
  • src/org/openstreetmap/josm/data/imagery/cache/CacheEntry.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.imagery.cache;
     3
     4import java.io.Serializable;
     5
     6public class CacheEntry implements Serializable {
     7    private static final long serialVersionUID = 1L; //version
     8    private byte[] content;
     9
     10    public CacheEntry(byte[] content) {
     11        this.content = content;
     12    }
     13
     14    public byte[] getContent() {
     15        return content;
     16    }
     17}
  • src/org/openstreetmap/josm/data/imagery/cache/CacheEntryAttributes.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.imagery.cache;
     3
     4import org.apache.commons.jcs.engine.ElementAttributes;
     5
     6public class CacheEntryAttributes extends ElementAttributes {
     7    private boolean noTileAtZoom = false;
     8    private String Etag = null;
     9    private long lastModification = 0;
     10    private long expirationTime = 0;
     11
     12
     13    public boolean isNoTileAtZoom() {
     14        return noTileAtZoom;
     15    }
     16    public void setNoTileAtZoom(boolean noTileAtZoom) {
     17        this.noTileAtZoom = noTileAtZoom;
     18    }
     19    public String getEtag() {
     20        return Etag;
     21    }
     22    public void setEtag(String etag) {
     23        Etag = etag;
     24    }
     25    public long getLastModification() {
     26        return lastModification;
     27    }
     28    public void setLastModification(long lastModification) {
     29        this.lastModification = lastModification;
     30    }
     31    public long getExpirationTime() {
     32        return expirationTime;
     33    }
     34    public void setExpirationTime(long expirationTime) {
     35        this.expirationTime = expirationTime;
     36    }
     37
     38}
  • src/org/openstreetmap/josm/data/imagery/cache/TileCacheManager.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.data.imagery.cache;
     3
     4import java.io.File;
     5import java.io.IOException;
     6import java.util.Properties;
     7
     8import org.apache.commons.jcs.access.CacheAccess;
     9import org.apache.commons.jcs.access.behavior.ICacheAccess;
     10import org.apache.commons.jcs.engine.control.CompositeCache;
     11import org.apache.commons.jcs.engine.control.CompositeCacheManager;
     12import org.openstreetmap.josm.Main;
     13
     14public abstract class TileCacheManager {
     15
     16    private static volatile CompositeCacheManager cacheManager = null;
     17    private static long maxObjectTTL        = 1000L * 60 * 60 * 24 * 31; // 31 days
     18
     19    // average tile size is about 20kb
     20    // 1000 is around 20MB under this assumptions
     21    private static long maxObjectsInMemory  = 1000;
     22    private static long maxObjectsOnDisk    = 100000;
     23
     24    private static void initialize() throws IOException {
     25        File cacheDir = new File(Main.pref.getCacheDirectory(), "jcs");
     26
     27        if ((!cacheDir.exists() && !cacheDir.mkdirs()))
     28            throw new IOException("Cannot access cache directory");
     29
     30        CompositeCacheManager cm  = CompositeCacheManager.getUnconfiguredInstance();
     31        Properties props = new Properties();
     32        props.setProperty("jcs.default", "DC");
     33        props.setProperty("jcs.default.cacheattributes",                            org.apache.commons.jcs.engine.CompositeCacheAttributes.class.getCanonicalName());
     34        props.setProperty("jcs.default.cacheattributes.MaxObjects",                 Long.toString(maxObjectsInMemory));
     35        props.setProperty("jcs.default.cacheattributes.UseMemoryShrinker",          "true");
     36        props.setProperty("jcs.default.cacheattributes.DiskUsagePatternName",       "UPDATE"); // store elements on disk on put
     37        props.setProperty("jcs.default.elementattributes",                          CacheEntryAttributes.class.getCanonicalName());
     38        props.setProperty("jcs.default.elementattributes.IsEternal",                "false");
     39        props.setProperty("jcs.default.elementattributes.MaxLife",                  Long.toString(maxObjectTTL));
     40        props.setProperty("jcs.default.elementattributes.IdleTime",                 Long.toString(maxObjectTTL));
     41        props.setProperty("jcs.default.elementattributes.IsSpool",                  "true");
     42
     43        props.setProperty("jcs.auxiliary.DC",                                       org.apache.commons.jcs.auxiliary.disk.block.BlockDiskCacheFactory.class.getCanonicalName());
     44        props.setProperty("jcs.auxiliary.DC.attributes",                            org.apache.commons.jcs.auxiliary.disk.block.BlockDiskCacheAttributes.class.getCanonicalName());
     45        props.setProperty("jcs.auxiliary.DC.attributes.DiskPath",                   cacheDir.getAbsolutePath());
     46        props.setProperty("jcs.auxiliary.DC.attributes.maxKeySize",                 Long.toString(maxObjectsOnDisk));
     47        props.setProperty("jcs.auxiliary.DC.attributes.blockSizeBytes",             "1024");
     48
     49
     50        cm.configure(props);
     51        cacheManager = cm;
     52    }
     53
     54    public static ICacheAccess<String, CacheEntry> getCache(String cacheName) throws IOException {
     55        if (cacheManager != null)
     56            return getCacheInner(cacheName);
     57
     58        synchronized (TileCacheManager.class) {
     59            if (cacheManager == null)
     60                initialize();
     61            return getCacheInner(cacheName);
     62        }
     63    }
     64
     65    private static ICacheAccess<String, CacheEntry> getCacheInner(String cacheName) {
     66        CompositeCache<String, CacheEntry> cc = cacheManager.getCache(cacheName);
     67        return new CacheAccess<String, CacheEntry>(cc);
     68    }
     69
     70}