001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data.cache; 003 004import java.util.Arrays; 005import java.util.Collections; 006import java.util.HashSet; 007import java.util.Map; 008import java.util.Map.Entry; 009import java.util.Set; 010import java.util.concurrent.ConcurrentHashMap; 011 012import org.apache.commons.jcs.engine.ElementAttributes; 013import org.openstreetmap.josm.Main; 014 015/** 016 * Class that contains attributes for JCS cache entries. Parameters are used to properly handle HTTP caching, 017 * and metadata structures, that should be stored together with the cache entry 018 * 019 * @author Wiktor Niesiobędzki 020 * @since 8168 021 */ 022public class CacheEntryAttributes extends ElementAttributes { 023 private static final long serialVersionUID = 1L; //version 024 private final Map<String, String> attrs = new ConcurrentHashMap<String, String>(RESERVED_KEYS.size()); 025 private static final String NO_TILE_AT_ZOOM = "noTileAtZoom"; 026 private static final String ETAG = "Etag"; 027 private static final String LAST_MODIFICATION = "lastModification"; 028 private static final String EXPIRATION_TIME = "expirationTime"; 029 private static final String HTTP_RESPONSE_CODE = "httpResponceCode"; 030 private static final String ERROR_MESSAGE = "errorMessage"; 031 // this contains all of the above 032 private static final Set<String> RESERVED_KEYS = new HashSet<>(Arrays.asList(new String[]{ 033 NO_TILE_AT_ZOOM, 034 ETAG, 035 LAST_MODIFICATION, 036 EXPIRATION_TIME, 037 HTTP_RESPONSE_CODE, 038 ERROR_MESSAGE 039 })); 040 041 042 /** 043 * Constructs a new {@code CacheEntryAttributes}. 044 */ 045 public CacheEntryAttributes() { 046 super(); 047 attrs.put(NO_TILE_AT_ZOOM, "false"); 048 attrs.put(LAST_MODIFICATION, "0"); 049 attrs.put(EXPIRATION_TIME, "0"); 050 attrs.put(HTTP_RESPONSE_CODE, "200"); 051 } 052 053 /** 054 * @return if the entry is marked as "no tile at this zoom level" 055 */ 056 public boolean isNoTileAtZoom() { 057 return Boolean.toString(true).equals(attrs.get(NO_TILE_AT_ZOOM)); 058 } 059 060 /** 061 * Sets the marker for "no tile at this zoom level" 062 * @param noTileAtZoom true if this entry is "no tile at this zoom level" 063 */ 064 public void setNoTileAtZoom(boolean noTileAtZoom) { 065 attrs.put(NO_TILE_AT_ZOOM, Boolean.toString(noTileAtZoom)); 066 } 067 068 /** 069 * @return ETag header value, that was returned for this entry. 070 */ 071 public String getEtag() { 072 return attrs.get(ETAG); 073 } 074 075 /** 076 * Sets the ETag header that was set with this entry 077 * @param etag Etag header 078 */ 079 public void setEtag(String etag) { 080 if (etag != null) { 081 attrs.put(ETAG, etag); 082 } 083 } 084 085 /** 086 * Utility for conversion from String to int, with default to 0, in case of any errors 087 * 088 * @param key - integer as string 089 * @return int value of the string 090 */ 091 private long getLongAttr(String key) { 092 String val = attrs.get(key); 093 if (val == null) { 094 attrs.put(key, "0"); 095 return 0; 096 } 097 try { 098 return Long.parseLong(val); 099 } catch (NumberFormatException e) { 100 attrs.put(key, "0"); 101 return 0; 102 } 103 } 104 105 /** 106 * @return last modification of the object in cache in milliseconds from Epoch 107 */ 108 public long getLastModification() { 109 return getLongAttr(LAST_MODIFICATION); 110 } 111 112 /** 113 * sets last modification of the object in cache 114 * 115 * @param lastModification time in format of milliseconds from Epoch 116 */ 117 public void setLastModification(long lastModification) { 118 attrs.put(LAST_MODIFICATION, Long.toString(lastModification)); 119 } 120 121 /** 122 * @return when the object expires in milliseconds from Epoch 123 */ 124 public long getExpirationTime() { 125 return getLongAttr(EXPIRATION_TIME); 126 } 127 128 /** 129 * sets expiration time for the object in cache 130 * 131 * @param expirationTime in format of milliseconds from epoch 132 */ 133 public void setExpirationTime(long expirationTime) { 134 attrs.put(EXPIRATION_TIME, Long.toString(expirationTime)); 135 } 136 137 /** 138 * Sets the HTTP response code that was sent with the cache entry 139 * 140 * @param responseCode http status code 141 * @since 8389 142 */ 143 public void setResponseCode(int responseCode) { 144 attrs.put(HTTP_RESPONSE_CODE, Integer.toString(responseCode)); 145 } 146 147 /** 148 * @return http status code 149 * @since 8389 150 */ 151 public int getResponseCode() { 152 return (int) getLongAttr(HTTP_RESPONSE_CODE); 153 } 154 155 /** 156 * Sets the metadata about cache entry. As it stores all data together, with other attributes 157 * in common map, some keys might not be stored. 158 * 159 * @param map metadata to save 160 * @since 8418 161 */ 162 public void setMetadata(Map<String, String> map) { 163 for (Entry<String, String> e: map.entrySet()) { 164 if (RESERVED_KEYS.contains(e.getKey())) { 165 Main.info("Metadata key configuration contains key {0} which is reserved for internal use"); 166 } else { 167 attrs.put(e.getKey(), e.getValue()); 168 } 169 } 170 } 171 172 /** 173 * Returns an unmodifiable Map containing all metadata. Unmodifiable prevents access to metadata within attributes. 174 * 175 * @return unmodifiable Map with cache element metadata 176 * @since 8418 177 */ 178 public Map<String, String> getMetadata() { 179 return Collections.unmodifiableMap(attrs); 180 } 181 182 /** 183 * @return error message returned while retrieving this object 184 */ 185 public String getErrorMessage() { 186 return attrs.get(ERROR_MESSAGE); 187 } 188 189 /** 190 * @param message error message related to this object 191 */ 192 public void setErrorMessage(String message) { 193 attrs.put(ERROR_MESSAGE, message); 194 } 195}