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

Last change on this file since 12851 was 12846, checked in by bastiK, 7 years ago

see #15229 - use Config.getPref() wherever possible

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