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

Last change on this file since 11348 was 11288, checked in by simon04, 7 years ago

see #13376 - Use TimeUnit instead of combinations of 1000/60/60/24

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