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

Last change on this file since 11193 was 10627, checked in by Don-vip, 8 years ago

sonar - squid:S1166 - Exception handlers should preserve the original exceptions

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