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

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

findbugs - EI_EXPOSE_REP

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