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

Last change on this file since 8347 was 8332, checked in by Don-vip, 9 years ago

fix various issues:

  • performance - inefficient use of keySet iterator instead of entrySet iterator
  • squid:S2674: The value returned from a stream read should be checked
  • remove unused imports
  • improve javadoc
  • Property svn:eol-style set to native
File size: 6.3 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,
17 * use CacheFiles
18 * @param <T> a {@link Throwable} that may be thrown during {@link #updateData()},
19 * use {@link RuntimeException} if no exception must be handled.
20 * @author xeen
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 = 60*60;
29 /** Update interval meaning an update is needed each day */
30 public static final int INTERVAL_DAILY = INTERVAL_HOURLY * 24;
31 /** Update interval meaning an update is needed each week */
32 public static final int INTERVAL_WEEKLY = INTERVAL_DAILY * 7;
33 /** Update interval meaning an update is needed each month */
34 public static final int INTERVAL_MONTHLY = INTERVAL_WEEKLY * 4;
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 = null;
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 */
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 return true;
100 }
101 }
102
103 protected void checkOfflineAccess() {
104 // To be overriden by subclasses
105 }
106
107 /**
108 * Updates data if required
109 * @return Returns the data
110 * @throws T if an error occurs
111 */
112 public byte[] updateIfRequired() throws T {
113 if (needsUpdate())
114 return updateForce();
115 return getData();
116 }
117
118 /**
119 * Updates data if required
120 * @return Returns the data as string
121 * @throws T if an error occurs
122 */
123 public String updateIfRequiredString() throws T {
124 if (needsUpdate())
125 return updateForceString();
126 return getDataString();
127 }
128
129 /**
130 * Executes an update regardless of updateInterval
131 * @return Returns the data
132 * @throws T if an error occurs
133 */
134 public byte[] updateForce() throws T {
135 this.data = updateData();
136 saveToDisk();
137 Main.pref.putInteger("cache." + ident, (int)(System.currentTimeMillis()/1000));
138 return data;
139 }
140
141 /**
142 * Executes an update regardless of updateInterval
143 * @return Returns the data as String
144 * @throws T if an error occurs
145 */
146 public String updateForceString() throws T {
147 updateForce();
148 return new String(data, StandardCharsets.UTF_8);
149 }
150
151 /**
152 * Returns the data without performing any updates
153 * @return the data
154 * @throws T if an error occurs
155 */
156 public byte[] getData() throws T {
157 if (data == null) {
158 loadFromDisk();
159 }
160 return data;
161 }
162
163 /**
164 * Returns the data without performing any updates
165 * @return the data as String
166 * @throws T if an error occurs
167 */
168 public String getDataString() throws T {
169 byte[] array = getData();
170 if (array == null) {
171 return null;
172 }
173 return new String(array, StandardCharsets.UTF_8);
174 }
175
176 /**
177 * Tries to load the data using the given ident from disk. If this fails, data will be updated, unless run in offline mode
178 */
179 private void loadFromDisk() throws T {
180 try (BufferedInputStream input = new BufferedInputStream(new FileInputStream(path))) {
181 this.data = new byte[input.available()];
182 if (input.read(this.data) < this.data.length) {
183 Main.error("Failed to read expected contents from "+path);
184 }
185 } catch (IOException e) {
186 if (!isOffline()) {
187 this.data = updateForce();
188 }
189 }
190 }
191
192 /**
193 * Stores the data to disk
194 */
195 private void saveToDisk() {
196 try (BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(path))) {
197 output.write(this.data);
198 output.flush();
199 } catch (IOException e) {
200 Main.error(e);
201 }
202 }
203
204 /**
205 * Flushes the data from memory. Class automatically reloads it from disk or updateData() if required
206 */
207 public void flushData() {
208 data = null;
209 }
210}
Note: See TracBrowser for help on using the repository browser.