]> git.basschouten.com Git - openhab-addons.git/blob
e2edd6b61567b1e82965a717a3ab9aa1c017faba
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.openweathermap.internal.utils;
14
15 import java.io.File;
16 import java.io.IOException;
17 import java.math.BigInteger;
18 import java.nio.charset.StandardCharsets;
19 import java.nio.file.Files;
20 import java.security.MessageDigest;
21 import java.security.NoSuchAlgorithmException;
22 import java.util.Arrays;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.core.OpenHAB;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * This is a simple file based cache implementation.
32  *
33  * @author Christoph Weitkamp - Initial contribution
34  */
35 @NonNullByDefault
36 public class ByteArrayFileCache {
37
38     private final Logger logger = LoggerFactory.getLogger(ByteArrayFileCache.class);
39
40     private static final String CACHE_FOLDER_NAME = "cache";
41     public static final char EXTENSION_SEPARATOR = '.';
42
43     protected final File cacheFolder;
44
45     public ByteArrayFileCache(String servicePID) {
46         // TODO track and limit folder size
47         // TODO support user specific folder
48         cacheFolder = new File(new File(new File(OpenHAB.getUserDataFolder()), CACHE_FOLDER_NAME), servicePID);
49         if (!cacheFolder.exists()) {
50             logger.debug("Creating cache folder '{}'", cacheFolder.getAbsolutePath());
51             cacheFolder.mkdirs();
52         }
53         logger.debug("Using cache folder '{}'", cacheFolder.getAbsolutePath());
54     }
55
56     /**
57      * Adds a file to the cache. If the cache previously contained a file for the key, the old file is replaced by the
58      * new content.
59      *
60      * @param key the key with which the file is to be associated
61      * @param content the content for the file to be associated with the specified key
62      */
63     public void put(String key, byte[] content) {
64         writeFile(getUniqueFile(key), content);
65     }
66
67     /**
68      * Adds a file to the cache.
69      *
70      * @param key the key with which the file is to be associated
71      * @param content the content for the file to be associated with the specified key
72      */
73     public void putIfAbsent(String key, byte[] content) {
74         File fileInCache = getUniqueFile(key);
75         if (fileInCache.exists()) {
76             logger.debug("File '{}' present in cache", fileInCache.getName());
77         } else {
78             writeFile(fileInCache, content);
79         }
80     }
81
82     /**
83      * Adds a file to the cache and returns the content of the file.
84      *
85      * @param key the key with which the file is to be associated
86      * @param content the content for the file to be associated with the specified key
87      * @return the content of the file associated with the given key
88      */
89     public byte[] putIfAbsentAndGet(String key, byte[] content) {
90         putIfAbsent(key, content);
91
92         // return get(key);
93         return content;
94     }
95
96     /**
97      * Writes the given content to the given {@link File}.
98      *
99      * @param fileInCache the {@link File}
100      * @param content the content to be written
101      */
102     private void writeFile(File fileInCache, byte[] content) {
103         logger.debug("Caching file '{}'", fileInCache.getName());
104         try {
105             Files.write(fileInCache.toPath(), content);
106         } catch (IOException e) {
107             logger.warn("Could not write file '{}' to cache", fileInCache.getName(), e);
108         }
109     }
110
111     /**
112      * Checks if the key is present in the cache.
113      *
114      * @param key the key whose presence in the cache is to be tested
115      * @return true if the cache contains a file for the specified key
116      */
117     public boolean containsKey(String key) {
118         return getUniqueFile(key).exists();
119     }
120
121     /**
122      * Removes the file associated with the given key from the cache.
123      *
124      * @param key the key whose associated file is to be removed
125      */
126     public void remove(String key) {
127         deleteFile(getUniqueFile(key));
128     }
129
130     /**
131      * Deletes the given {@link File}.
132      *
133      * @param fileInCache the {@link File}
134      */
135     private void deleteFile(File fileInCache) {
136         if (fileInCache.exists()) {
137             logger.debug("Deleting file '{}' from cache", fileInCache.getName());
138             fileInCache.delete();
139         } else {
140             logger.debug("File '{}' not found in cache", fileInCache.getName());
141         }
142     }
143
144     /**
145      * Removes all files from the cache.
146      */
147     public void clear() {
148         File[] filesInCache = cacheFolder.listFiles();
149         if (filesInCache != null && filesInCache.length > 0) {
150             logger.debug("Deleting all files from cache");
151             Arrays.stream(filesInCache).forEach(File::delete);
152         }
153     }
154
155     /**
156      * Returns the content of the file associated with the given key, if it is present.
157      *
158      * @param key the key whose associated file is to be returned
159      * @return the content of the file associated with the given key
160      */
161     public byte[] get(String key) {
162         return readFile(getUniqueFile(key));
163     }
164
165     /**
166      * Reads the content from the given {@link File}, if it is present.
167      *
168      * @param fileInCache the {@link File}
169      * @return the content of the file
170      */
171     private byte[] readFile(File fileInCache) {
172         if (fileInCache.exists()) {
173             logger.debug("Reading file '{}' from cache", fileInCache.getName());
174             try {
175                 return Files.readAllBytes(fileInCache.toPath());
176             } catch (IOException e) {
177                 logger.warn("Could not read file '{}' from cache", fileInCache.getName(), e);
178             }
179         } else {
180             logger.debug("File '{}' not found in cache", fileInCache.getName());
181         }
182         return new byte[0];
183     }
184
185     /**
186      * Creates a unique {@link File} from the key with which the file is to be associated.
187      *
188      * @param key the key with which the file is to be associated
189      * @return unique file for the file associated with the given key
190      */
191     private File getUniqueFile(String key) {
192         // TODO: store / cache file internally for faster operations
193         String fileExtension = getFileExtension(key);
194         return new File(cacheFolder,
195                 getUniqueFileName(key) + (fileExtension == null ? "" : EXTENSION_SEPARATOR + fileExtension));
196     }
197
198     /**
199      * Gets the extension of a file name.
200      *
201      * @param fileName the file name to retrieve the extension of
202      * @return the extension of the file or null if none exists
203      */
204     private @Nullable String getFileExtension(String fileName) {
205         int index = fileName.lastIndexOf(EXTENSION_SEPARATOR);
206         // exclude file names starting with a dot
207         if (index > 0) {
208             return fileName.substring(index + 1);
209         } else {
210             return null;
211         }
212     }
213
214     /**
215      * Creates a unique file name from the key with which the file is to be associated.
216      *
217      * @param key the key with which the file is to be associated
218      * @return unique file name for the file associated with the given key
219      */
220     private String getUniqueFileName(String key) {
221         try {
222             byte[] bytesOfFileName = key.getBytes(StandardCharsets.UTF_8);
223             MessageDigest md = MessageDigest.getInstance("MD5");
224             byte[] md5Hash = md.digest(bytesOfFileName);
225             BigInteger bigInt = new BigInteger(1, md5Hash);
226             StringBuilder fileNameHash = new StringBuilder(bigInt.toString(16));
227             // Now we need to zero pad it if you actually want the full 32 chars
228             while (fileNameHash.length() < 32) {
229                 fileNameHash.insert(0, "0");
230             }
231             return fileNameHash.toString();
232         } catch (NoSuchAlgorithmException ex) {
233             // should not happen
234             logger.error("Could not create MD5 hash for key '{}'", key, ex);
235             return key.toString();
236         }
237     }
238 }