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

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

sonar - fb-contrib:ACEM_ABSTRACT_CLASS_EMPTY_METHODS

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