source: josm/trunk/src/org/openstreetmap/josm/io/CacheCustomContent.java@ 12620

Last change on this file since 12620 was 12620, checked in by Don-vip, 7 years ago

see #15182 - deprecate all Main logging methods and introduce suitable replacements in Logging for most of them

  • Property svn:eol-style set to native
File size: 6.9 KB
RevLine 
[1450]1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io;
3
4import java.io.BufferedInputStream;
5import java.io.BufferedOutputStream;
6import java.io.File;
7import java.io.FileInputStream;
8import java.io.FileOutputStream;
[2986]9import java.io.IOException;
[7082]10import java.nio.charset.StandardCharsets;
[11288]11import java.util.concurrent.TimeUnit;
[1450]12
13import org.openstreetmap.josm.Main;
[12620]14import org.openstreetmap.josm.tools.Logging;
[11879]15import org.openstreetmap.josm.tools.Utils;
[1450]16
[1610]17/**
18 * Use this class if you want to cache and store a single file that gets updated regularly.
[8419]19 * Unless you flush() it will be kept in memory. If you want to cache a lot of data and/or files, use CacheFiles.
20 * @author xeen
[4709]21 * @param <T> a {@link Throwable} that may be thrown during {@link #updateData()},
22 * use {@link RuntimeException} if no exception must be handled.
[8332]23 * @since 1450
[1610]24 */
[4709]25public abstract class CacheCustomContent<T extends Throwable> {
[8332]26
27 /** Update interval meaning an update is always needed */
[6889]28 public static final int INTERVAL_ALWAYS = -1;
[8332]29 /** Update interval meaning an update is needed each hour */
[11288]30 public static final int INTERVAL_HOURLY = (int) TimeUnit.HOURS.toSeconds(1);
[8332]31 /** Update interval meaning an update is needed each day */
[11288]32 public static final int INTERVAL_DAILY = (int) TimeUnit.DAYS.toSeconds(1);
[8332]33 /** Update interval meaning an update is needed each week */
[11288]34 public static final int INTERVAL_WEEKLY = (int) TimeUnit.DAYS.toSeconds(7);
[8332]35 /** Update interval meaning an update is needed each month */
[11288]36 public static final int INTERVAL_MONTHLY = (int) TimeUnit.DAYS.toSeconds(28);
[8332]37 /** Update interval meaning an update is never needed */
[6889]38 public static final int INTERVAL_NEVER = Integer.MAX_VALUE;
[1610]39
[1450]40 /**
[1610]41 * Where the data will be stored
[1450]42 */
[8840]43 private byte[] data;
[1610]44
[1450]45 /**
46 * The ident that identifies the stored file. Includes file-ending.
47 */
[6889]48 private final String ident;
[1610]49
[1450]50 /**
51 * The (file-)path where the data will be stored
52 */
[6889]53 private final File path;
[1610]54
[1450]55 /**
56 * How often to update the cached version
57 */
[6889]58 private final int updateInterval;
[1610]59
[1450]60 /**
61 * This function will be executed when an update is required. It has to be implemented by the
62 * inheriting class and should use a worker if it has a long wall time as the function is
63 * executed in the current thread.
64 * @return the data to cache
[8926]65 * @throws T a {@link Throwable}
[1450]66 */
[4709]67 protected abstract byte[] updateData() throws T;
[1610]68
[1450]69 /**
70 * Initializes the class. Note that all read data will be stored in memory until it is flushed
[1610]71 * by flushData().
[8332]72 * @param ident ident that identifies the stored file. Includes file-ending.
73 * @param updateInterval update interval in seconds. -1 means always
[1450]74 */
75 public CacheCustomContent(String ident, int updateInterval) {
76 this.ident = ident;
77 this.updateInterval = updateInterval;
[4810]78 this.path = new File(Main.pref.getCacheDirectory(), ident);
[1450]79 }
[1610]80
[8332]81 /**
82 * This function serves as a comfort hook to perform additional checks if the cache is valid
83 * @return True if the cached copy is still valid
84 */
85 protected boolean isCacheValid() {
86 return true;
87 }
88
[7434]89 private boolean needsUpdate() {
90 if (isOffline()) {
91 return false;
92 }
[11288]93 return Main.pref.getInteger("cache." + ident, 0) + updateInterval < TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())
[7434]94 || !isCacheValid();
95 }
96
97 private boolean isOffline() {
98 try {
99 checkOfflineAccess();
100 return false;
101 } catch (OfflineAccessException e) {
[12620]102 Logging.trace(e);
[7434]103 return true;
104 }
105 }
106
[11510]107 /**
108 * Ensures underlying resource is not accessed in offline mode.
109 * @throws OfflineAccessException if resource is accessed in offline mode
110 */
111 protected abstract void checkOfflineAccess();
[7434]112
[1450]113 /**
114 * Updates data if required
115 * @return Returns the data
[8332]116 * @throws T if an error occurs
[1450]117 */
[4709]118 public byte[] updateIfRequired() throws T {
[7434]119 if (needsUpdate())
[1450]120 return updateForce();
121 return getData();
122 }
[1610]123
[1450]124 /**
125 * Updates data if required
126 * @return Returns the data as string
[8332]127 * @throws T if an error occurs
[1450]128 */
[4709]129 public String updateIfRequiredString() throws T {
[7434]130 if (needsUpdate())
[1450]131 return updateForceString();
132 return getDataString();
133 }
[1610]134
[1450]135 /**
136 * Executes an update regardless of updateInterval
137 * @return Returns the data
[8332]138 * @throws T if an error occurs
[1450]139 */
[11879]140 private byte[] updateForce() throws T {
[1450]141 this.data = updateData();
142 saveToDisk();
[11288]143 Main.pref.putInteger("cache." + ident, (int) (TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
[1450]144 return data;
145 }
[1610]146
[1450]147 /**
148 * Executes an update regardless of updateInterval
149 * @return Returns the data as String
[8332]150 * @throws T if an error occurs
[1450]151 */
[4709]152 public String updateForceString() throws T {
[1450]153 updateForce();
[7082]154 return new String(data, StandardCharsets.UTF_8);
[1450]155 }
156
157 /**
158 * Returns the data without performing any updates
159 * @return the data
[8332]160 * @throws T if an error occurs
[1450]161 */
[4709]162 public byte[] getData() throws T {
[4810]163 if (data == null) {
[1450]164 loadFromDisk();
[2986]165 }
[11879]166 return Utils.copyArray(data);
[1450]167 }
[1610]168
[1450]169 /**
170 * Returns the data without performing any updates
171 * @return the data as String
[8332]172 * @throws T if an error occurs
[1450]173 */
[4709]174 public String getDataString() throws T {
[7434]175 byte[] array = getData();
176 if (array == null) {
177 return null;
178 }
179 return new String(array, StandardCharsets.UTF_8);
[1450]180 }
181
182 /**
[7434]183 * Tries to load the data using the given ident from disk. If this fails, data will be updated, unless run in offline mode
[8926]184 * @throws T a {@link Throwable}
[1450]185 */
[4709]186 private void loadFromDisk() throws T {
[7033]187 try (BufferedInputStream input = new BufferedInputStream(new FileInputStream(path))) {
[7026]188 this.data = new byte[input.available()];
[8332]189 if (input.read(this.data) < this.data.length) {
[12620]190 Logging.error("Failed to read expected contents from "+path);
[8332]191 }
[7026]192 } catch (IOException e) {
[12620]193 Logging.trace(e);
[7434]194 if (!isOffline()) {
195 this.data = updateForce();
196 }
[1450]197 }
198 }
[1610]199
[1450]200 /**
201 * Stores the data to disk
202 */
203 private void saveToDisk() {
[7033]204 try (BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(path))) {
[1450]205 output.write(this.data);
206 output.flush();
[7033]207 } catch (IOException e) {
[12620]208 Logging.error(e);
[2986]209 }
[1610]210 }
211
[1450]212 /**
[7434]213 * Flushes the data from memory. Class automatically reloads it from disk or updateData() if required
[1450]214 */
215 public void flushData() {
216 data = null;
217 }
218}
Note: See TracBrowser for help on using the repository browser.