// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.data.cache;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.jcs.engine.ElementAttributes;
import org.openstreetmap.josm.Main;
/**
* Class that contains attributes for JCS cache entries. Parameters are used to properly handle HTTP caching,
* and metadata structures, that should be stored together with the cache entry
*
* @author Wiktor Niesiobędzki
* @since 8168
*/
public class CacheEntryAttributes extends ElementAttributes {
private static final long serialVersionUID = 1L; //version
private final Map<String, String> attrs = new ConcurrentHashMap<>(RESERVED_KEYS.size());
private static final String NO_TILE_AT_ZOOM = "noTileAtZoom";
private static final String ETAG = "Etag";
private static final String LAST_MODIFICATION = "lastModification";
private static final String EXPIRATION_TIME = "expirationTime";
private static final String HTTP_RESPONSE_CODE = "httpResponceCode";
private static final String ERROR_MESSAGE = "errorMessage";
// this contains all of the above
private static final Set<String> RESERVED_KEYS = new HashSet<>(Arrays.asList(new String[]{
NO_TILE_AT_ZOOM,
ETAG,
LAST_MODIFICATION,
EXPIRATION_TIME,
HTTP_RESPONSE_CODE,
ERROR_MESSAGE
}));
/**
* Constructs a new {@code CacheEntryAttributes}.
*/
public CacheEntryAttributes() {
super();
attrs.put(NO_TILE_AT_ZOOM, "false");
attrs.put(LAST_MODIFICATION, "0");
attrs.put(EXPIRATION_TIME, "0");
attrs.put(HTTP_RESPONSE_CODE, "200");
}
/**
* @return if the entry is marked as "no tile at this zoom level"
*/
public boolean isNoTileAtZoom() {
return Boolean.toString(true).equals(attrs.get(NO_TILE_AT_ZOOM));
}
/**
* Sets the marker for "no tile at this zoom level"
* @param noTileAtZoom true if this entry is "no tile at this zoom level"
*/
public void setNoTileAtZoom(boolean noTileAtZoom) {
attrs.put(NO_TILE_AT_ZOOM, Boolean.toString(noTileAtZoom));
}
/**
* @return ETag header value, that was returned for this entry.
*/
public String getEtag() {
return attrs.get(ETAG);
}
/**
* Sets the ETag header that was set with this entry
* @param etag Etag header
*/
public void setEtag(String etag) {
if (etag != null) {
attrs.put(ETAG, etag);
}
}
/**
* Utility for conversion from String to int, with default to 0, in case of any errors
*
* @param key - integer as string
* @return int value of the string
*/
private long getLongAttr(String key) {
String val = attrs.get(key);
if (val == null) {
attrs.put(key, "0");
return 0;
}
try {
return Long.parseLong(val);
} catch (NumberFormatException e) {
attrs.put(key, "0");
return 0;
}
}
/**
* @return last modification of the object in cache in milliseconds from Epoch
*/
public long getLastModification() {
return getLongAttr(LAST_MODIFICATION);
}
/**
* sets last modification of the object in cache
*
* @param lastModification time in format of milliseconds from Epoch
*/
public void setLastModification(long lastModification) {
attrs.put(LAST_MODIFICATION, Long.toString(lastModification));
}
/**
* @return when the object expires in milliseconds from Epoch
*/
public long getExpirationTime() {
return getLongAttr(EXPIRATION_TIME);
}
/**
* sets expiration time for the object in cache
*
* @param expirationTime in format of milliseconds from epoch
*/
public void setExpirationTime(long expirationTime) {
attrs.put(EXPIRATION_TIME, Long.toString(expirationTime));
}
/**
* Sets the HTTP response code that was sent with the cache entry
*
* @param responseCode http status code
* @since 8389
*/
public void setResponseCode(int responseCode) {
attrs.put(HTTP_RESPONSE_CODE, Integer.toString(responseCode));
}
/**
* @return http status code
* @since 8389
*/
public int getResponseCode() {
return (int) getLongAttr(HTTP_RESPONSE_CODE);
}
/**
* Sets the metadata about cache entry. As it stores all data together, with other attributes
* in common map, some keys might not be stored.
*
* @param map metadata to save
* @since 8418
*/
public void setMetadata(Map<String, String> map) {
for (Entry<String, String> e: map.entrySet()) {
if (RESERVED_KEYS.contains(e.getKey())) {
Main.info("Metadata key configuration contains key {0} which is reserved for internal use");
} else {
attrs.put(e.getKey(), e.getValue());
}
}
}
/**
* Returns an unmodifiable Map containing all metadata. Unmodifiable prevents access to metadata within attributes.
*
* @return unmodifiable Map with cache element metadata
* @since 8418
*/
public Map<String, String> getMetadata() {
return Collections.unmodifiableMap(attrs);
}
/**
* @return error message returned while retrieving this object
*/
public String getErrorMessage() {
return attrs.get(ERROR_MESSAGE);
}
/**
* @param error error related to this object
* @since 10469
*/
public void setError(Exception error) {
setErrorMessage(Main.getErrorMessage(error));
}
/**
* @param message error message related to this object
*/
public void setErrorMessage(String message) {
attrs.put(ERROR_MESSAGE, message);
}
}