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

Last change on this file since 11083 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
RevLine 
[1450]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;
[2986]9import java.io.IOException;
[7082]10import java.nio.charset.StandardCharsets;
[1450]11
12import org.openstreetmap.josm.Main;
13
[1610]14/**
15 * Use this class if you want to cache and store a single file that gets updated regularly.
[8419]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
[4709]18 * @param <T> a {@link Throwable} that may be thrown during {@link #updateData()},
19 * use {@link RuntimeException} if no exception must be handled.
[8332]20 * @since 1450
[1610]21 */
[4709]22public abstract class CacheCustomContent<T extends Throwable> {
[8332]23
24 /** Update interval meaning an update is always needed */
[6889]25 public static final int INTERVAL_ALWAYS = -1;
[8332]26 /** Update interval meaning an update is needed each hour */
[6889]27 public static final int INTERVAL_HOURLY = 60*60;
[8332]28 /** Update interval meaning an update is needed each day */
[6889]29 public static final int INTERVAL_DAILY = INTERVAL_HOURLY * 24;
[8332]30 /** Update interval meaning an update is needed each week */
[6889]31 public static final int INTERVAL_WEEKLY = INTERVAL_DAILY * 7;
[8332]32 /** Update interval meaning an update is needed each month */
[6889]33 public static final int INTERVAL_MONTHLY = INTERVAL_WEEKLY * 4;
[8332]34 /** Update interval meaning an update is never needed */
[6889]35 public static final int INTERVAL_NEVER = Integer.MAX_VALUE;
[1610]36
[1450]37 /**
[1610]38 * Where the data will be stored
[1450]39 */
[8840]40 private byte[] data;
[1610]41
[1450]42 /**
43 * The ident that identifies the stored file. Includes file-ending.
44 */
[6889]45 private final String ident;
[1610]46
[1450]47 /**
48 * The (file-)path where the data will be stored
49 */
[6889]50 private final File path;
[1610]51
[1450]52 /**
53 * How often to update the cached version
54 */
[6889]55 private final int updateInterval;
[1610]56
[1450]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
[8926]62 * @throws T a {@link Throwable}
[1450]63 */
[4709]64 protected abstract byte[] updateData() throws T;
[1610]65
[1450]66 /**
67 * Initializes the class. Note that all read data will be stored in memory until it is flushed
[1610]68 * by flushData().
[8332]69 * @param ident ident that identifies the stored file. Includes file-ending.
70 * @param updateInterval update interval in seconds. -1 means always
[1450]71 */
72 public CacheCustomContent(String ident, int updateInterval) {
73 this.ident = ident;
74 this.updateInterval = updateInterval;
[4810]75 this.path = new File(Main.pref.getCacheDirectory(), ident);
[1450]76 }
[1610]77
[8332]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
[7434]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) {
[10627]99 Main.trace(e);
[7434]100 return true;
101 }
102 }
103
104 protected void checkOfflineAccess() {
105 // To be overriden by subclasses
106 }
107
[1450]108 /**
109 * Updates data if required
110 * @return Returns the data
[8332]111 * @throws T if an error occurs
[1450]112 */
[4709]113 public byte[] updateIfRequired() throws T {
[7434]114 if (needsUpdate())
[1450]115 return updateForce();
116 return getData();
117 }
[1610]118
[1450]119 /**
120 * Updates data if required
121 * @return Returns the data as string
[8332]122 * @throws T if an error occurs
[1450]123 */
[4709]124 public String updateIfRequiredString() throws T {
[7434]125 if (needsUpdate())
[1450]126 return updateForceString();
127 return getDataString();
128 }
[1610]129
[1450]130 /**
131 * Executes an update regardless of updateInterval
132 * @return Returns the data
[8332]133 * @throws T if an error occurs
[1450]134 */
[4709]135 public byte[] updateForce() throws T {
[1450]136 this.data = updateData();
137 saveToDisk();
[8510]138 Main.pref.putInteger("cache." + ident, (int) (System.currentTimeMillis()/1000));
[1450]139 return data;
140 }
[1610]141
[1450]142 /**
143 * Executes an update regardless of updateInterval
144 * @return Returns the data as String
[8332]145 * @throws T if an error occurs
[1450]146 */
[4709]147 public String updateForceString() throws T {
[1450]148 updateForce();
[7082]149 return new String(data, StandardCharsets.UTF_8);
[1450]150 }
151
152 /**
153 * Returns the data without performing any updates
154 * @return the data
[8332]155 * @throws T if an error occurs
[1450]156 */
[4709]157 public byte[] getData() throws T {
[4810]158 if (data == null) {
[1450]159 loadFromDisk();
[2986]160 }
[1450]161 return data;
162 }
[1610]163
[1450]164 /**
165 * Returns the data without performing any updates
166 * @return the data as String
[8332]167 * @throws T if an error occurs
[1450]168 */
[4709]169 public String getDataString() throws T {
[7434]170 byte[] array = getData();
171 if (array == null) {
172 return null;
173 }
174 return new String(array, StandardCharsets.UTF_8);
[1450]175 }
176
177 /**
[7434]178 * Tries to load the data using the given ident from disk. If this fails, data will be updated, unless run in offline mode
[8926]179 * @throws T a {@link Throwable}
[1450]180 */
[4709]181 private void loadFromDisk() throws T {
[7033]182 try (BufferedInputStream input = new BufferedInputStream(new FileInputStream(path))) {
[7026]183 this.data = new byte[input.available()];
[8332]184 if (input.read(this.data) < this.data.length) {
185 Main.error("Failed to read expected contents from "+path);
186 }
[7026]187 } catch (IOException e) {
[10627]188 Main.trace(e);
[7434]189 if (!isOffline()) {
190 this.data = updateForce();
191 }
[1450]192 }
193 }
[1610]194
[1450]195 /**
196 * Stores the data to disk
197 */
198 private void saveToDisk() {
[7033]199 try (BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(path))) {
[1450]200 output.write(this.data);
201 output.flush();
[7033]202 } catch (IOException e) {
[6643]203 Main.error(e);
[2986]204 }
[1610]205 }
206
[1450]207 /**
[7434]208 * Flushes the data from memory. Class automatically reloads it from disk or updateData() if required
[1450]209 */
210 public void flushData() {
211 data = null;
212 }
213}
Note: See TracBrowser for help on using the repository browser.