diff --git a/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastStatusUpdater.java b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastStatusUpdater.java index a293e5053..61a1f24ed 100644 --- a/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastStatusUpdater.java +++ b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/ChromecastStatusUpdater.java @@ -26,7 +26,7 @@ import java.util.Map; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.openhab.binding.chromecast.internal.handler.ChromecastHandler; -import org.openhab.binding.chromecast.internal.utils.ByteArrayFileCache; +import org.openhab.core.cache.ByteArrayFileCache; import org.openhab.core.io.net.http.HttpUtil; import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; @@ -63,6 +63,7 @@ import su.litvak.chromecast.api.v2.Volume; */ @NonNullByDefault public class ChromecastStatusUpdater { + private final Logger logger = LoggerFactory.getLogger(ChromecastStatusUpdater.class); private final Thing thing; diff --git a/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/utils/ByteArrayFileCache.java b/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/utils/ByteArrayFileCache.java deleted file mode 100644 index da8bc5918..000000000 --- a/bundles/org.openhab.binding.chromecast/src/main/java/org/openhab/binding/chromecast/internal/utils/ByteArrayFileCache.java +++ /dev/null @@ -1,305 +0,0 @@ -/** - * Copyright (c) 2010-2020 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.chromecast.internal.utils; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.core.OpenHAB; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This is a simple file based cache implementation. - * - * @author Christoph Weitkamp - Initial contribution - */ -@NonNullByDefault -public class ByteArrayFileCache { - - private final Logger logger = LoggerFactory.getLogger(ByteArrayFileCache.class); - - private static final String MD5_ALGORITHM = "MD5"; - - static final String CACHE_FOLDER_NAME = "cache"; - private static final char EXTENSION_SEPARATOR = '.'; - private static final char UNIX_SEPARATOR = '/'; - private static final char WINDOWS_SEPARATOR = '\\'; - - private final File cacheFolder; - - static final long ONE_DAY_IN_MILLIS = TimeUnit.DAYS.toMillis(1); - private int expiry = 0; - - private static final Map FILES_IN_CACHE = new ConcurrentHashMap<>(); - - /** - * Creates a new {@link ByteArrayFileCache} instance for a service. Creates a cache folder under - * $userdata/cache/$servicePID. - * - * @param servicePID PID of the service - */ - public ByteArrayFileCache(String servicePID) { - // TODO track and limit folder size - // TODO support user specific folder - cacheFolder = new File(new File(OpenHAB.getUserDataFolder(), CACHE_FOLDER_NAME), servicePID); - if (!cacheFolder.exists()) { - logger.debug("Creating cache folder '{}'", cacheFolder.getAbsolutePath()); - cacheFolder.mkdirs(); - } - logger.debug("Using cache folder '{}'", cacheFolder.getAbsolutePath()); - } - - /** - * Creates a new {@link ByteArrayFileCache} instance for a service. Creates a cache folder under - * $userdata/cache/$servicePID/. - * - * @param servicePID PID of the service - * @param int the days for how long the files stay in the cache valid. Must be positive. 0 to - * disables this functionality. - */ - public ByteArrayFileCache(String servicePID, int expiry) { - this(servicePID); - if (expiry < 0) { - throw new IllegalArgumentException("Cache expiration time must be greater than or equal to 0"); - } - this.expiry = expiry; - } - - /** - * Adds a file to the cache. If the cache previously contained a file for the key, the old file is replaced by the - * new content. - * - * @param key the key with which the file is to be associated - * @param content the content for the file to be associated with the specified key - */ - public void put(String key, byte[] content) { - writeFile(getUniqueFile(key), content); - } - - /** - * Adds a file to the cache. - * - * @param key the key with which the file is to be associated - * @param content the content for the file to be associated with the specified key - */ - public void putIfAbsent(String key, byte[] content) { - File fileInCache = getUniqueFile(key); - if (fileInCache.exists()) { - logger.debug("File '{}' present in cache", fileInCache.getName()); - // update time of last use - fileInCache.setLastModified(System.currentTimeMillis()); - } else { - writeFile(fileInCache, content); - } - } - - /** - * Adds a file to the cache and returns the content of the file. - * - * @param key the key with which the file is to be associated - * @param content the content for the file to be associated with the specified key - * @return the content of the file associated with the given key - */ - public byte[] putIfAbsentAndGet(String key, byte[] content) { - putIfAbsent(key, content); - - return content; - } - - /** - * Writes the given content to the given {@link File}. - * - * @param fileInCache the {@link File} - * @param content the content to be written - */ - private void writeFile(File fileInCache, byte[] content) { - logger.debug("Caching file '{}'", fileInCache.getName()); - try { - Files.write(fileInCache.toPath(), content); - } catch (IOException e) { - logger.warn("Could not write file '{}' to cache", fileInCache.getName(), e); - } - } - - /** - * Checks if the key is present in the cache. - * - * @param key the key whose presence in the cache is to be tested - * @return true if the cache contains a file for the specified key - */ - public boolean containsKey(String key) { - return getUniqueFile(key).exists(); - } - - /** - * Removes the file associated with the given key from the cache. - * - * @param key the key whose associated file is to be removed - */ - public void remove(String key) { - deleteFile(getUniqueFile(key)); - } - - /** - * Deletes the given {@link File}. - * - * @param fileInCache the {@link File} - */ - private void deleteFile(File fileInCache) { - if (fileInCache.exists()) { - logger.debug("Deleting file '{}' from cache", fileInCache.getName()); - fileInCache.delete(); - } else { - logger.debug("File '{}' not found in cache", fileInCache.getName()); - } - } - - /** - * Removes all files from the cache. - */ - public void clear() { - File[] filesInCache = cacheFolder.listFiles(); - if (filesInCache != null && filesInCache.length > 0) { - logger.debug("Deleting all files from cache"); - Arrays.stream(filesInCache).forEach(File::delete); - } - } - - /** - * Removes expired files from the cache. - */ - public void clearExpired() { - // exit if expiry is set to 0 (disabled) - if (expiry <= 0) { - return; - } - File[] filesInCache = cacheFolder.listFiles(); - if (filesInCache != null && filesInCache.length > 0) { - logger.debug("Deleting expired files from cache"); - Arrays.stream(filesInCache).filter(file -> isExpired(file)).forEach(File::delete); - } - } - - /** - * Checks if the given {@link File} is expired. - * - * @param fileInCache the {@link File} - * @return true if the file is expired, false otherwise - */ - private boolean isExpired(File fileInCache) { - // exit if expiry is set to 0 (disabled) - if (expiry <= 0) { - return false; - } - return expiry * ONE_DAY_IN_MILLIS < System.currentTimeMillis() - fileInCache.lastModified(); - } - - /** - * Returns the content of the file associated with the given key, if it is present. - * - * @param key the key whose associated file is to be returned - * @return the content of the file associated with the given key - * @throws FileNotFoundException if the given file could not be found in cache - * @throws IOException if an I/O error occurs reading the given file - */ - public byte[] get(String key) throws FileNotFoundException, IOException { - return readFile(getUniqueFile(key)); - } - - /** - * Reads the content from the given {@link File}, if it is present. - * - * @param fileInCache the {@link File} - * @return the content of the file - * @throws FileNotFoundException if the given file could not be found in cache - * @throws IOException if an I/O error occurs reading the given file - */ - private byte[] readFile(File fileInCache) throws FileNotFoundException, IOException { - if (fileInCache.exists()) { - logger.debug("Reading file '{}' from cache", fileInCache.getName()); - // update time of last use - fileInCache.setLastModified(System.currentTimeMillis()); - try { - return Files.readAllBytes(fileInCache.toPath()); - } catch (IOException e) { - logger.warn("Could not read file '{}' from cache", fileInCache.getName(), e); - throw new IOException(String.format("Could not read file '%s' from cache", fileInCache.getName())); - } - } else { - logger.debug("File '{}' not found in cache", fileInCache.getName()); - throw new FileNotFoundException(String.format("File '%s' not found in cache", fileInCache.getName())); - } - } - - /** - * Creates a unique {@link File} from the key with which the file is to be associated. - * - * @param key the key with which the file is to be associated - * @return unique file for the file associated with the given key - */ - File getUniqueFile(String key) { - String uniqueFileName = getUniqueFileName(key); - if (FILES_IN_CACHE.containsKey(uniqueFileName)) { - return FILES_IN_CACHE.get(uniqueFileName); - } else { - String fileExtension = getFileExtension(key); - File fileInCache = new File(cacheFolder, - uniqueFileName + (fileExtension == null ? "" : EXTENSION_SEPARATOR + fileExtension)); - FILES_IN_CACHE.put(uniqueFileName, fileInCache); - return fileInCache; - } - } - - /** - * Gets the extension of a file name. - * - * @param fileName the file name to retrieve the extension of - * @return the extension of the file or null if none exists - */ - @Nullable - String getFileExtension(String fileName) { - int extensionPos = fileName.lastIndexOf(EXTENSION_SEPARATOR); - int lastSeparatorPos = Math.max(fileName.lastIndexOf(UNIX_SEPARATOR), fileName.lastIndexOf(WINDOWS_SEPARATOR)); - return lastSeparatorPos > extensionPos ? null : fileName.substring(extensionPos + 1).replaceFirst("\\?.*$", ""); - } - - /** - * Creates a unique file name from the key with which the file is to be associated. - * - * @param key the key with which the file is to be associated - * @return unique file name for the file associated with the given key - */ - String getUniqueFileName(String key) { - try { - final MessageDigest md = MessageDigest.getInstance(MD5_ALGORITHM); - return String.format("%032x", new BigInteger(1, md.digest(key.getBytes(StandardCharsets.UTF_8)))); - } catch (NoSuchAlgorithmException ex) { - // should not happen - logger.error("Could not create MD5 hash for key '{}'", key, ex); - return key; - } - } -} diff --git a/bundles/org.openhab.binding.darksky/src/main/java/org/openhab/binding/darksky/internal/connection/DarkSkyConnection.java b/bundles/org.openhab.binding.darksky/src/main/java/org/openhab/binding/darksky/internal/connection/DarkSkyConnection.java index ba7f3951c..5adbe8ce3 100644 --- a/bundles/org.openhab.binding.darksky/src/main/java/org/openhab/binding/darksky/internal/connection/DarkSkyConnection.java +++ b/bundles/org.openhab.binding.darksky/src/main/java/org/openhab/binding/darksky/internal/connection/DarkSkyConnection.java @@ -16,6 +16,7 @@ import static java.util.stream.Collectors.joining; import static org.eclipse.jetty.http.HttpMethod.GET; import static org.eclipse.jetty.http.HttpStatus.*; +import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -34,7 +35,7 @@ import org.eclipse.jetty.client.api.ContentResponse; import org.openhab.binding.darksky.internal.config.DarkSkyAPIConfiguration; import org.openhab.binding.darksky.internal.handler.DarkSkyAPIHandler; import org.openhab.binding.darksky.internal.model.DarkSkyJsonWeatherData; -import org.openhab.binding.darksky.internal.utils.ByteArrayFileCache; +import org.openhab.core.cache.ByteArrayFileCache; import org.openhab.core.cache.ExpiringCacheMap; import org.openhab.core.io.net.http.HttpUtil; import org.openhab.core.library.types.PointType; @@ -126,7 +127,12 @@ public class DarkSkyConnection { private static @Nullable RawType downloadWeatherIconFromCache(String url) { if (IMAGE_CACHE.containsKey(url)) { - return new RawType(IMAGE_CACHE.get(url), PNG_CONTENT_TYPE); + try { + return new RawType(IMAGE_CACHE.get(url), PNG_CONTENT_TYPE); + } catch (IOException e) { + LoggerFactory.getLogger(DarkSkyConnection.class).trace("Failed to download the content of URL '{}'", + url, e); + } } else { RawType image = downloadWeatherIcon(url); if (image != null) { diff --git a/bundles/org.openhab.binding.darksky/src/main/java/org/openhab/binding/darksky/internal/utils/ByteArrayFileCache.java b/bundles/org.openhab.binding.darksky/src/main/java/org/openhab/binding/darksky/internal/utils/ByteArrayFileCache.java deleted file mode 100644 index 2bc89ff05..000000000 --- a/bundles/org.openhab.binding.darksky/src/main/java/org/openhab/binding/darksky/internal/utils/ByteArrayFileCache.java +++ /dev/null @@ -1,307 +0,0 @@ -/** - * Copyright (c) 2010-2020 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.darksky.internal.utils; - -import java.io.File; -import java.io.IOException; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.core.OpenHAB; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This is a simple file based cache implementation. - * - * @author Christoph Weitkamp - Initial contribution - */ -@NonNullByDefault -public class ByteArrayFileCache { - - private final Logger logger = LoggerFactory.getLogger(ByteArrayFileCache.class); - - private static final String MD5_ALGORITHM = "MD5"; - - static final String CACHE_FOLDER_NAME = "cache"; - private static final char EXTENSION_SEPARATOR = '.'; - private static final char UNIX_SEPARATOR = '/'; - private static final char WINDOWS_SEPARATOR = '\\'; - - private final File cacheFolder; - - static final long ONE_DAY_IN_MILLIS = TimeUnit.DAYS.toMillis(1); - private int expiry = 0; - - private static final Map FILES_IN_CACHE = new ConcurrentHashMap<>(); - - /** - * Creates a new {@link ByteArrayFileCache} instance for a service. Creates a cache folder under - * $userdata/cache/$servicePID. - * - * @param servicePID PID of the service - */ - public ByteArrayFileCache(String servicePID) { - // TODO track and limit folder size - // TODO support user specific folder - cacheFolder = new File(new File(OpenHAB.getUserDataFolder(), CACHE_FOLDER_NAME), servicePID); - if (!cacheFolder.exists()) { - logger.debug("Creating cache folder '{}'", cacheFolder.getAbsolutePath()); - cacheFolder.mkdirs(); - } - logger.debug("Using cache folder '{}'", cacheFolder.getAbsolutePath()); - } - - /** - * Creates a new {@link ByteArrayFileCache} instance for a service. Creates a cache folder under - * $userdata/cache/$servicePID/. - * - * @param servicePID PID of the service - * @param int the days for how long the files stay in the cache valid. Must be positive. 0 to - * disables this functionality. - */ - public ByteArrayFileCache(String servicePID, int expiry) { - this(servicePID); - if (expiry < 0) { - throw new IllegalArgumentException("Cache expiration time must be greater than or equal to 0"); - } - this.expiry = expiry; - } - - /** - * Adds a file to the cache. If the cache previously contained a file for the key, the old file is replaced by the - * new content. - * - * @param key the key with which the file is to be associated - * @param content the content for the file to be associated with the specified key - */ - public void put(String key, byte[] content) { - writeFile(getUniqueFile(key), content); - } - - /** - * Adds a file to the cache. - * - * @param key the key with which the file is to be associated - * @param content the content for the file to be associated with the specified key - */ - public void putIfAbsent(String key, byte[] content) { - File fileInCache = getUniqueFile(key); - if (fileInCache.exists()) { - logger.debug("File '{}' present in cache", fileInCache.getName()); - // update time of last use - fileInCache.setLastModified(System.currentTimeMillis()); - } else { - writeFile(fileInCache, content); - } - } - - /** - * Adds a file to the cache and returns the content of the file. - * - * @param key the key with which the file is to be associated - * @param content the content for the file to be associated with the specified key - * @return the content of the file associated with the given key - */ - public byte[] putIfAbsentAndGet(String key, byte[] content) { - putIfAbsent(key, content); - - return content; - } - - /** - * Writes the given content to the given {@link File}. - * - * @param fileInCache the {@link File} - * @param content the content to be written - */ - private void writeFile(File fileInCache, byte[] content) { - logger.debug("Caching file '{}'", fileInCache.getName()); - try { - Files.write(fileInCache.toPath(), content); - } catch (IOException e) { - logger.warn("Could not write file '{}' to cache", fileInCache.getName(), e); - } - } - - /** - * Checks if the key is present in the cache. - * - * @param key the key whose presence in the cache is to be tested - * @return true if the cache contains a file for the specified key - */ - public boolean containsKey(String key) { - return getUniqueFile(key).exists(); - } - - /** - * Removes the file associated with the given key from the cache. - * - * @param key the key whose associated file is to be removed - */ - public void remove(String key) { - deleteFile(getUniqueFile(key)); - } - - /** - * Deletes the given {@link File}. - * - * @param fileInCache the {@link File} - */ - private void deleteFile(File fileInCache) { - if (fileInCache.exists()) { - logger.debug("Deleting file '{}' from cache", fileInCache.getName()); - fileInCache.delete(); - } else { - logger.debug("File '{}' not found in cache", fileInCache.getName()); - } - } - - /** - * Removes all files from the cache. - */ - public void clear() { - File[] filesInCache = cacheFolder.listFiles(); - if (filesInCache != null && filesInCache.length > 0) { - logger.debug("Deleting all files from cache"); - Arrays.stream(filesInCache).forEach(File::delete); - } - } - - /** - * Removes expired files from the cache. - */ - public void clearExpired() { - // exit if expiry is set to 0 (disabled) - if (expiry <= 0) { - return; - } - File[] filesInCache = cacheFolder.listFiles(); - if (filesInCache != null && filesInCache.length > 0) { - logger.debug("Deleting expired files from cache"); - Arrays.stream(filesInCache).filter(file -> isExpired(file)).forEach(File::delete); - } - } - - /** - * Checks if the given {@link File} is expired. - * - * @param fileInCache the {@link File} - * @return true if the file is expired, false otherwise - */ - private boolean isExpired(File fileInCache) { - // exit if expiry is set to 0 (disabled) - if (expiry <= 0) { - return false; - } - return expiry * ONE_DAY_IN_MILLIS < System.currentTimeMillis() - fileInCache.lastModified(); - } - - /** - * Returns the content of the file associated with the given key, if it is present. - * - * @param key the key whose associated file is to be returned - * @return the content of the file associated with the given key - */ - public byte[] get(String key) { - return readFile(getUniqueFile(key)); - } - - /** - * Reads the content from the given {@link File}, if it is present. - * - * @param fileInCache the {@link File} - * @return the content of the file - */ - private byte[] readFile(File fileInCache) { - if (fileInCache.exists()) { - logger.debug("Reading file '{}' from cache", fileInCache.getName()); - // update time of last use - fileInCache.setLastModified(System.currentTimeMillis()); - try { - return Files.readAllBytes(fileInCache.toPath()); - } catch (IOException e) { - logger.warn("Could not read file '{}' from cache", fileInCache.getName(), e); - } - } else { - logger.debug("File '{}' not found in cache", fileInCache.getName()); - } - return new byte[0]; - } - - /** - * Creates a unique {@link File} from the key with which the file is to be associated. - * - * @param key the key with which the file is to be associated - * @return unique file for the file associated with the given key - */ - File getUniqueFile(String key) { - String uniqueFileName = getUniqueFileName(key); - if (FILES_IN_CACHE.containsKey(uniqueFileName)) { - return FILES_IN_CACHE.get(uniqueFileName); - } else { - String fileExtension = getFileExtension(key); - File fileInCache = new File(cacheFolder, - uniqueFileName + (fileExtension == null ? "" : EXTENSION_SEPARATOR + fileExtension)); - FILES_IN_CACHE.put(uniqueFileName, fileInCache); - return fileInCache; - } - } - - /** - * Gets the extension of a file name. - * - * @param fileName the file name to retrieve the extension of - * @return the extension of the file or null if none exists - */ - @Nullable - String getFileExtension(String fileName) { - int extensionPos = fileName.lastIndexOf(EXTENSION_SEPARATOR); - int lastSeparatorPos = Math.max(fileName.lastIndexOf(UNIX_SEPARATOR), fileName.lastIndexOf(WINDOWS_SEPARATOR)); - return lastSeparatorPos > extensionPos ? null : fileName.substring(extensionPos + 1); - } - - /** - * Creates a unique file name from the key with which the file is to be associated. - * - * @param key the key with which the file is to be associated - * @return unique file name for the file associated with the given key - */ - String getUniqueFileName(String key) { - try { - MessageDigest md = MessageDigest.getInstance(MD5_ALGORITHM); - byte[] bytesOfKey = key.getBytes(StandardCharsets.UTF_8); - byte[] md5Hash = md.digest(bytesOfKey); - BigInteger bigInt = new BigInteger(1, md5Hash); - String fileNameHash = bigInt.toString(16); - // Now we need to zero pad it if you actually want the full 32 chars - while (fileNameHash.length() < 32) { - fileNameHash = "0" + fileNameHash; - } - return fileNameHash; - } catch (NoSuchAlgorithmException ex) { - // should not happen - logger.error("Could not create MD5 hash for key '{}'", key, ex); - return key; - } - } -} diff --git a/bundles/org.openhab.binding.darksky/src/test/java/org/openhab/binding/darksky/internal/utils/ByteArrayFileCacheTest.java b/bundles/org.openhab.binding.darksky/src/test/java/org/openhab/binding/darksky/internal/utils/ByteArrayFileCacheTest.java deleted file mode 100644 index a0d8bd4c3..000000000 --- a/bundles/org.openhab.binding.darksky/src/test/java/org/openhab/binding/darksky/internal/utils/ByteArrayFileCacheTest.java +++ /dev/null @@ -1,177 +0,0 @@ -/** - * Copyright (c) 2010-2020 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.darksky.internal.utils; - -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.openhab.core.OpenHAB; - -/** - * Test class for the {@link ByteArrayFileCache} class. - * - * @author Christoph Weitkamp - Initial contribution - */ -public class ByteArrayFileCacheTest { - - private static final String SERVICE_PID = "org.openhab.binding.darksky"; - - private static final File USERDATA_FOLDER = new File(OpenHAB.getUserDataFolder()); - private static final File CACHE_FOLDER = new File(USERDATA_FOLDER, ByteArrayFileCache.CACHE_FOLDER_NAME); - private static final File SERVICE_CACHE_FOLDER = new File(CACHE_FOLDER, SERVICE_PID); - - private static final String MP3_FILE_NAME = SERVICE_CACHE_FOLDER.getAbsolutePath() + "doorbell.mp3"; - private static final String TXT_FILE_NAME = SERVICE_CACHE_FOLDER.getAbsolutePath() + "doorbell.txt"; - - private static final byte[] EMPTY_BUFFER = new byte[0]; - - private ByteArrayFileCache subject; - - @BeforeEach - public void setUp() { - subject = new ByteArrayFileCache(SERVICE_PID); - } - - @AfterEach - public void tearDown() { - // delete all files - subject.clear(); - } - - @AfterAll - public static void cleanUp() { - // delete all folders - SERVICE_CACHE_FOLDER.delete(); - CACHE_FOLDER.delete(); - USERDATA_FOLDER.delete(); - } - - @Test - public void testGetFileExtension() { - assertThat(subject.getFileExtension("/var/log/openhab2/"), is(nullValue())); - assertThat(subject.getFileExtension("/var/log/foo.bar/"), is(nullValue())); - assertThat(subject.getFileExtension("doorbell.mp3"), is(equalTo("mp3"))); - assertThat(subject.getFileExtension("/tmp/doorbell.mp3"), is(equalTo("mp3"))); - assertThat(subject.getFileExtension(MP3_FILE_NAME), is(equalTo("mp3"))); - assertThat(subject.getFileExtension(TXT_FILE_NAME), is(equalTo("txt"))); - assertThat(subject.getFileExtension("/var/log/openhab2/.."), is("")); - assertThat(subject.getFileExtension(".hidden"), is(equalTo("hidden"))); - assertThat(subject.getFileExtension("C:\\Program Files (x86)\\java\\bin\\javaw.exe"), is(equalTo("exe"))); - assertThat(subject.getFileExtension("https://www.youtube.com/watch?v=qYrpPrLY868"), is(nullValue())); - } - - @Test - public void testGetUniqueFileName() { - String mp3UniqueFileName = subject.getUniqueFileName(MP3_FILE_NAME); - assertThat(mp3UniqueFileName, is(equalTo(subject.getUniqueFileName(MP3_FILE_NAME)))); - - String txtUniqueFileName = subject.getUniqueFileName(TXT_FILE_NAME); - assertThat(txtUniqueFileName, is(equalTo(subject.getUniqueFileName(TXT_FILE_NAME)))); - - assertThat(mp3UniqueFileName, is(not(equalTo(txtUniqueFileName)))); - } - - @Test - public void testGet() { - assertThat(subject.get(MP3_FILE_NAME), is(equalTo(EMPTY_BUFFER))); - } - - @Test - public void testPut() throws IOException { - byte[] buffer = readFile(); - subject.put(MP3_FILE_NAME, buffer); - - assertThat(subject.get(MP3_FILE_NAME), is(equalTo(buffer))); - } - - @Test - public void testPutIfAbsent() throws IOException { - byte[] buffer = readFile(); - subject.putIfAbsent(MP3_FILE_NAME, buffer); - - assertThat(subject.get(MP3_FILE_NAME), is(equalTo(buffer))); - } - - @Test - public void testPutIfAbsentAndGet() throws IOException { - byte[] buffer = readFile(); - - assertThat(subject.putIfAbsentAndGet(MP3_FILE_NAME, buffer), is(equalTo(buffer))); - } - - @Test - public void testContainsKey() throws IOException { - assertThat(subject.containsKey(MP3_FILE_NAME), is(false)); - - subject.put(MP3_FILE_NAME, readFile()); - - assertThat(subject.containsKey(MP3_FILE_NAME), is(true)); - } - - @Test - public void testRemove() throws IOException { - subject.put(MP3_FILE_NAME, readFile()); - subject.remove(MP3_FILE_NAME); - - assertThat(subject.get(MP3_FILE_NAME), is(equalTo(EMPTY_BUFFER))); - } - - @Test - public void testClear() throws IOException { - subject.put(MP3_FILE_NAME, readFile()); - subject.clear(); - - assertThat(subject.get(MP3_FILE_NAME), is(equalTo(EMPTY_BUFFER))); - } - - @Test - public void clearExpiredClearsNothing() throws IOException { - byte[] buffer = readFile(); - subject.put(MP3_FILE_NAME, buffer); - subject.clearExpired(); - - assertThat(subject.get(MP3_FILE_NAME), is(equalTo(buffer))); - } - - @Test - public void clearExpired() throws IOException { - subject = new ByteArrayFileCache(SERVICE_PID, 1); - - subject.put(MP3_FILE_NAME, readFile()); - - // manipulate time of last use - File fileInCache = subject.getUniqueFile(MP3_FILE_NAME); - fileInCache.setLastModified(System.currentTimeMillis() - 2 * ByteArrayFileCache.ONE_DAY_IN_MILLIS); - - subject.clearExpired(); - - assertThat(subject.get(MP3_FILE_NAME), is(equalTo(EMPTY_BUFFER))); - } - - private byte[] readFile() throws IOException { - byte[] buffer; - try (InputStream is = ByteArrayFileCacheTest.class.getResourceAsStream("/sounds/doorbell.mp3")) { - buffer = new byte[is.available()]; - is.read(buffer); - } - return buffer; - } -} diff --git a/bundles/org.openhab.binding.darksky/src/test/resources/sounds/doorbell.mp3 b/bundles/org.openhab.binding.darksky/src/test/resources/sounds/doorbell.mp3 deleted file mode 100644 index 7a8a5a365..000000000 Binary files a/bundles/org.openhab.binding.darksky/src/test/resources/sounds/doorbell.mp3 and /dev/null differ diff --git a/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/protocol/KodiConnection.java b/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/protocol/KodiConnection.java index bd51cb913..a1afeb163 100644 --- a/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/protocol/KodiConnection.java +++ b/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/protocol/KodiConnection.java @@ -40,7 +40,7 @@ import org.openhab.binding.kodi.internal.model.KodiSubtitle; import org.openhab.binding.kodi.internal.model.KodiSystemProperties; import org.openhab.binding.kodi.internal.model.KodiUniqueID; import org.openhab.binding.kodi.internal.model.KodiVideoStream; -import org.openhab.binding.kodi.internal.utils.ByteArrayFileCache; +import org.openhab.core.cache.ByteArrayFileCache; import org.openhab.core.cache.ExpiringCacheMap; import org.openhab.core.io.net.http.HttpUtil; import org.openhab.core.library.types.RawType; @@ -891,10 +891,14 @@ public class KodiConnection implements KodiClientSocketEventListener { private @Nullable RawType downloadImageFromCache(String url) { if (IMAGE_CACHE.containsKey(url)) { - byte[] bytes = IMAGE_CACHE.get(url); - String contentType = HttpUtil.guessContentTypeFromData(bytes); - return new RawType(bytes, - contentType == null || contentType.isEmpty() ? RawType.DEFAULT_MIME_TYPE : contentType); + try { + byte[] bytes = IMAGE_CACHE.get(url); + String contentType = HttpUtil.guessContentTypeFromData(bytes); + return new RawType(bytes, + contentType == null || contentType.isEmpty() ? RawType.DEFAULT_MIME_TYPE : contentType); + } catch (IOException e) { + logger.trace("Failed to download the content of URL '{}'", url, e); + } } else { RawType image = downloadImage(url); if (image != null) { diff --git a/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/utils/ByteArrayFileCache.java b/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/utils/ByteArrayFileCache.java deleted file mode 100644 index 9eced3e4a..000000000 --- a/bundles/org.openhab.binding.kodi/src/main/java/org/openhab/binding/kodi/internal/utils/ByteArrayFileCache.java +++ /dev/null @@ -1,307 +0,0 @@ -/** - * Copyright (c) 2010-2020 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.kodi.internal.utils; - -import java.io.File; -import java.io.IOException; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.core.OpenHAB; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This is a simple file based cache implementation. - * - * @author Christoph Weitkamp - Initial contribution - */ -@NonNullByDefault -public class ByteArrayFileCache { - - private final Logger logger = LoggerFactory.getLogger(ByteArrayFileCache.class); - - private static final String MD5_ALGORITHM = "MD5"; - - static final String CACHE_FOLDER_NAME = "cache"; - private static final char EXTENSION_SEPARATOR = '.'; - private static final char UNIX_SEPARATOR = '/'; - private static final char WINDOWS_SEPARATOR = '\\'; - - private final File cacheFolder; - - static final long ONE_DAY_IN_MILLIS = TimeUnit.DAYS.toMillis(1); - private int expiry = 0; - - private static final Map FILES_IN_CACHE = new ConcurrentHashMap<>(); - - /** - * Creates a new {@link ByteArrayFileCache} instance for a service. Creates a cache folder under - * $userdata/cache/$servicePID. - * - * @param servicePID PID of the service - */ - public ByteArrayFileCache(String servicePID) { - // TODO track and limit folder size - // TODO support user specific folder - cacheFolder = new File(new File(OpenHAB.getUserDataFolder(), CACHE_FOLDER_NAME), servicePID); - if (!cacheFolder.exists()) { - logger.debug("Creating cache folder '{}'", cacheFolder.getAbsolutePath()); - cacheFolder.mkdirs(); - } - logger.debug("Using cache folder '{}'", cacheFolder.getAbsolutePath()); - } - - /** - * Creates a new {@link ByteArrayFileCache} instance for a service. Creates a cache folder under - * $userdata/cache/$servicePID/. - * - * @param servicePID PID of the service - * @param int the days for how long the files stay in the cache valid. Must be positive. 0 to - * disables this functionality. - */ - public ByteArrayFileCache(String servicePID, int expiry) { - this(servicePID); - if (expiry < 0) { - throw new IllegalArgumentException("Cache expiration time must be greater than or equal to 0"); - } - this.expiry = expiry; - } - - /** - * Adds a file to the cache. If the cache previously contained a file for the key, the old file is replaced by the - * new content. - * - * @param key the key with which the file is to be associated - * @param content the content for the file to be associated with the specified key - */ - public void put(String key, byte[] content) { - writeFile(getUniqueFile(key), content); - } - - /** - * Adds a file to the cache. - * - * @param key the key with which the file is to be associated - * @param content the content for the file to be associated with the specified key - */ - public void putIfAbsent(String key, byte[] content) { - File fileInCache = getUniqueFile(key); - if (fileInCache.exists()) { - logger.debug("File '{}' present in cache", fileInCache.getName()); - // update time of last use - fileInCache.setLastModified(System.currentTimeMillis()); - } else { - writeFile(fileInCache, content); - } - } - - /** - * Adds a file to the cache and returns the content of the file. - * - * @param key the key with which the file is to be associated - * @param content the content for the file to be associated with the specified key - * @return the content of the file associated with the given key - */ - public byte[] putIfAbsentAndGet(String key, byte[] content) { - putIfAbsent(key, content); - - return content; - } - - /** - * Writes the given content to the given {@link File}. - * - * @param fileInCache the {@link File} - * @param content the content to be written - */ - private void writeFile(File fileInCache, byte[] content) { - logger.debug("Caching file '{}'", fileInCache.getName()); - try { - Files.write(fileInCache.toPath(), content); - } catch (IOException e) { - logger.warn("Could not write file '{}' to cache", fileInCache.getName(), e); - } - } - - /** - * Checks if the key is present in the cache. - * - * @param key the key whose presence in the cache is to be tested - * @return true if the cache contains a file for the specified key - */ - public boolean containsKey(String key) { - return getUniqueFile(key).exists(); - } - - /** - * Removes the file associated with the given key from the cache. - * - * @param key the key whose associated file is to be removed - */ - public void remove(String key) { - deleteFile(getUniqueFile(key)); - } - - /** - * Deletes the given {@link File}. - * - * @param fileInCache the {@link File} - */ - private void deleteFile(File fileInCache) { - if (fileInCache.exists()) { - logger.debug("Deleting file '{}' from cache", fileInCache.getName()); - fileInCache.delete(); - } else { - logger.debug("File '{}' not found in cache", fileInCache.getName()); - } - } - - /** - * Removes all files from the cache. - */ - public void clear() { - File[] filesInCache = cacheFolder.listFiles(); - if (filesInCache != null && filesInCache.length > 0) { - logger.debug("Deleting all files from cache"); - Arrays.stream(filesInCache).forEach(File::delete); - } - } - - /** - * Removes expired files from the cache. - */ - public void clearExpired() { - // exit if expiry is set to 0 (disabled) - if (expiry <= 0) { - return; - } - File[] filesInCache = cacheFolder.listFiles(); - if (filesInCache != null && filesInCache.length > 0) { - logger.debug("Deleting expired files from cache"); - Arrays.stream(filesInCache).filter(file -> isExpired(file)).forEach(File::delete); - } - } - - /** - * Checks if the given {@link File} is expired. - * - * @param fileInCache the {@link File} - * @return true if the file is expired, false otherwise - */ - private boolean isExpired(File fileInCache) { - // exit if expiry is set to 0 (disabled) - if (expiry <= 0) { - return false; - } - return expiry * ONE_DAY_IN_MILLIS < System.currentTimeMillis() - fileInCache.lastModified(); - } - - /** - * Returns the content of the file associated with the given key, if it is present. - * - * @param key the key whose associated file is to be returned - * @return the content of the file associated with the given key - */ - public byte[] get(String key) { - return readFile(getUniqueFile(key)); - } - - /** - * Reads the content from the given {@link File}, if it is present. - * - * @param fileInCache the {@link File} - * @return the content of the file - */ - private byte[] readFile(File fileInCache) { - if (fileInCache.exists()) { - logger.debug("Reading file '{}' from cache", fileInCache.getName()); - // update time of last use - fileInCache.setLastModified(System.currentTimeMillis()); - try { - return Files.readAllBytes(fileInCache.toPath()); - } catch (IOException e) { - logger.warn("Could not read file '{}' from cache", fileInCache.getName(), e); - } - } else { - logger.debug("File '{}' not found in cache", fileInCache.getName()); - } - return new byte[0]; - } - - /** - * Creates a unique {@link File} from the key with which the file is to be associated. - * - * @param key the key with which the file is to be associated - * @return unique file for the file associated with the given key - */ - File getUniqueFile(String key) { - String uniqueFileName = getUniqueFileName(key); - if (FILES_IN_CACHE.containsKey(uniqueFileName)) { - return FILES_IN_CACHE.get(uniqueFileName); - } else { - String fileExtension = getFileExtension(key); - File fileInCache = new File(cacheFolder, - uniqueFileName + (fileExtension == null ? "" : EXTENSION_SEPARATOR + fileExtension)); - FILES_IN_CACHE.put(uniqueFileName, fileInCache); - return fileInCache; - } - } - - /** - * Gets the extension of a file name. - * - * @param fileName the file name to retrieve the extension of - * @return the extension of the file or null if none exists - */ - @Nullable - String getFileExtension(String fileName) { - int extensionPos = fileName.lastIndexOf(EXTENSION_SEPARATOR); - int lastSeparatorPos = Math.max(fileName.lastIndexOf(UNIX_SEPARATOR), fileName.lastIndexOf(WINDOWS_SEPARATOR)); - return lastSeparatorPos > extensionPos ? null : fileName.substring(extensionPos + 1).replaceFirst("\\?.*$", ""); - } - - /** - * Creates a unique file name from the key with which the file is to be associated. - * - * @param key the key with which the file is to be associated - * @return unique file name for the file associated with the given key - */ - String getUniqueFileName(String key) { - try { - MessageDigest md = MessageDigest.getInstance(MD5_ALGORITHM); - byte[] bytesOfKey = key.getBytes(StandardCharsets.UTF_8); - byte[] md5Hash = md.digest(bytesOfKey); - BigInteger bigInt = new BigInteger(1, md5Hash); - String fileNameHash = bigInt.toString(16); - // We need to zero pad it if you actually want the full 32 chars - while (fileNameHash.length() < 32) { - fileNameHash = "0" + fileNameHash; - } - return fileNameHash; - } catch (NoSuchAlgorithmException ex) { - // should not happen - logger.error("Could not create MD5 hash for key '{}'", key, ex); - return key; - } - } -} diff --git a/bundles/org.openhab.binding.kodi/src/test/java/org/openhab/binding/kodi/internal/utils/ByteArrayFileCacheTest.java b/bundles/org.openhab.binding.kodi/src/test/java/org/openhab/binding/kodi/internal/utils/ByteArrayFileCacheTest.java deleted file mode 100644 index cee8c42cb..000000000 --- a/bundles/org.openhab.binding.kodi/src/test/java/org/openhab/binding/kodi/internal/utils/ByteArrayFileCacheTest.java +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Copyright (c) 2010-2020 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.kodi.internal.utils; - -import static org.hamcrest.CoreMatchers.*; -import static org.hamcrest.MatcherAssert.assertThat; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.openhab.core.OpenHAB; - -/** - * Test class for the {@link ByteArrayFileCache} class. - * - * @author Christoph Weitkamp - Initial contribution - */ -public class ByteArrayFileCacheTest { - - private static final String SERVICE_PID = "org.openhab.binding.kodi"; - - private static final File USERDATA_FOLDER = new File(OpenHAB.getUserDataFolder()); - private static final File CACHE_FOLDER = new File(USERDATA_FOLDER, ByteArrayFileCache.CACHE_FOLDER_NAME); - private static final File SERVICE_CACHE_FOLDER = new File(CACHE_FOLDER, SERVICE_PID); - - private static final String MP3_FILE_NAME = SERVICE_CACHE_FOLDER.getAbsolutePath() + "doorbell.mp3"; - private static final String TXT_FILE_NAME = SERVICE_CACHE_FOLDER.getAbsolutePath() + "doorbell.txt"; - - private static final byte[] EMPTY_BUFFER = new byte[0]; - - private ByteArrayFileCache subject; - - @BeforeEach - public void setUp() { - subject = new ByteArrayFileCache(SERVICE_PID); - } - - @AfterEach - public void tearDown() { - // delete all files - subject.clear(); - } - - @AfterAll - public static void cleanUp() { - // delete all folders - SERVICE_CACHE_FOLDER.delete(); - CACHE_FOLDER.delete(); - USERDATA_FOLDER.delete(); - } - - @Test - public void testGetFileExtension() { - assertThat(subject.getFileExtension("/var/log/openhab2/"), is(nullValue())); - assertThat(subject.getFileExtension("/var/log/foo.bar/"), is(nullValue())); - assertThat(subject.getFileExtension("doorbell.mp3"), is(equalTo("mp3"))); - assertThat(subject.getFileExtension("/tmp/doorbell.mp3"), is(equalTo("mp3"))); - assertThat(subject.getFileExtension(MP3_FILE_NAME), is(equalTo("mp3"))); - assertThat(subject.getFileExtension(TXT_FILE_NAME), is(equalTo("txt"))); - assertThat(subject.getFileExtension("/var/log/openhab2/.."), is("")); - assertThat(subject.getFileExtension(".hidden"), is(equalTo("hidden"))); - assertThat(subject.getFileExtension("C:\\Program Files (x86)\\java\\bin\\javaw.exe"), is(equalTo("exe"))); - assertThat(subject.getFileExtension("https://www.youtube.com/watch?v=qYrpPrLY868"), is(nullValue())); - assertThat(subject.getFileExtension( - "a52fc16cca77ab2bf6abe51e463ce501.jpg?response-content-type=image%2Fjpeg&test=1"), is("jpg")); - } - - @Test - public void testGetUniqueFileNameIsUnique() { - String mp3UniqueFileName = subject.getUniqueFileName(MP3_FILE_NAME); - assertThat(mp3UniqueFileName, is(equalTo(subject.getUniqueFileName(MP3_FILE_NAME)))); - - String txtUniqueFileName = subject.getUniqueFileName(TXT_FILE_NAME); - assertThat(txtUniqueFileName, is(equalTo(subject.getUniqueFileName(TXT_FILE_NAME)))); - - assertThat(mp3UniqueFileName, is(not(equalTo(txtUniqueFileName)))); - } - - @Test - public void testGet() { - assertThat(subject.get(MP3_FILE_NAME), is(equalTo(EMPTY_BUFFER))); - } - - @Test - public void testPut() throws IOException { - byte[] buffer = readFile(); - subject.put(MP3_FILE_NAME, buffer); - - assertThat(subject.get(MP3_FILE_NAME), is(equalTo(buffer))); - } - - @Test - public void testPutIfAbsent() throws IOException { - byte[] buffer = readFile(); - subject.putIfAbsent(MP3_FILE_NAME, buffer); - - assertThat(subject.get(MP3_FILE_NAME), is(equalTo(buffer))); - } - - @Test - public void testPutIfAbsentAndGet() throws IOException { - byte[] buffer = readFile(); - - assertThat(subject.putIfAbsentAndGet(MP3_FILE_NAME, buffer), is(equalTo(buffer))); - } - - @Test - public void testContainsKey() throws IOException { - assertThat(subject.containsKey(MP3_FILE_NAME), is(false)); - - subject.put(MP3_FILE_NAME, readFile()); - - assertThat(subject.containsKey(MP3_FILE_NAME), is(true)); - } - - @Test - public void testRemove() throws IOException { - subject.put(MP3_FILE_NAME, readFile()); - subject.remove(MP3_FILE_NAME); - - assertThat(subject.get(MP3_FILE_NAME), is(equalTo(EMPTY_BUFFER))); - } - - @Test - public void testClear() throws IOException { - subject.put(MP3_FILE_NAME, readFile()); - subject.clear(); - - assertThat(subject.get(MP3_FILE_NAME), is(equalTo(EMPTY_BUFFER))); - } - - @Test - public void clearExpiredClearsNothing() throws IOException { - byte[] buffer = readFile(); - subject.put(MP3_FILE_NAME, buffer); - subject.clearExpired(); - - assertThat(subject.get(MP3_FILE_NAME), is(equalTo(buffer))); - } - - @Test - public void clearExpired() throws IOException { - subject = new ByteArrayFileCache(SERVICE_PID, 1); - - subject.put(MP3_FILE_NAME, readFile()); - - // manipulate time of last use - File fileInCache = subject.getUniqueFile(MP3_FILE_NAME); - fileInCache.setLastModified(System.currentTimeMillis() - 2 * ByteArrayFileCache.ONE_DAY_IN_MILLIS); - - subject.clearExpired(); - - assertThat(subject.get(MP3_FILE_NAME), is(equalTo(EMPTY_BUFFER))); - } - - private byte[] readFile() throws IOException { - byte[] buffer; - try (InputStream is = ByteArrayFileCacheTest.class.getResourceAsStream("/sounds/doorbell.mp3")) { - buffer = new byte[is.available()]; - is.read(buffer); - } - return buffer; - } -} diff --git a/bundles/org.openhab.binding.kodi/src/test/resources/sounds/doorbell.mp3 b/bundles/org.openhab.binding.kodi/src/test/resources/sounds/doorbell.mp3 deleted file mode 100644 index 7a8a5a365..000000000 Binary files a/bundles/org.openhab.binding.kodi/src/test/resources/sounds/doorbell.mp3 and /dev/null differ diff --git a/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/connection/OpenWeatherMapConnection.java b/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/connection/OpenWeatherMapConnection.java index d82fb0bd3..d2705e68a 100644 --- a/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/connection/OpenWeatherMapConnection.java +++ b/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/connection/OpenWeatherMapConnection.java @@ -16,6 +16,7 @@ import static java.util.stream.Collectors.joining; import static org.eclipse.jetty.http.HttpMethod.GET; import static org.eclipse.jetty.http.HttpStatus.*; +import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -38,7 +39,7 @@ import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonHourlyF import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonUVIndexData; import org.openhab.binding.openweathermap.internal.dto.OpenWeatherMapJsonWeatherData; import org.openhab.binding.openweathermap.internal.handler.OpenWeatherMapAPIHandler; -import org.openhab.binding.openweathermap.internal.utils.ByteArrayFileCache; +import org.openhab.core.cache.ByteArrayFileCache; import org.openhab.core.cache.ExpiringCacheMap; import org.openhab.core.io.net.http.HttpUtil; import org.openhab.core.library.types.PointType; @@ -224,7 +225,12 @@ public class OpenWeatherMapConnection { private static @Nullable RawType downloadWeatherIconFromCache(String url) { if (IMAGE_CACHE.containsKey(url)) { - return new RawType(IMAGE_CACHE.get(url), PNG_CONTENT_TYPE); + try { + return new RawType(IMAGE_CACHE.get(url), PNG_CONTENT_TYPE); + } catch (IOException e) { + LoggerFactory.getLogger(OpenWeatherMapConnection.class) + .trace("Failed to download the content of URL '{}'", url, e); + } } else { RawType image = downloadWeatherIcon(url); if (image != null) { diff --git a/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/utils/ByteArrayFileCache.java b/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/utils/ByteArrayFileCache.java deleted file mode 100644 index e2edd6b61..000000000 --- a/bundles/org.openhab.binding.openweathermap/src/main/java/org/openhab/binding/openweathermap/internal/utils/ByteArrayFileCache.java +++ /dev/null @@ -1,238 +0,0 @@ -/** - * Copyright (c) 2010-2020 Contributors to the openHAB project - * - * See the NOTICE file(s) distributed with this work for additional - * information. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.openhab.binding.openweathermap.internal.utils; - -import java.io.File; -import java.io.IOException; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - -import org.eclipse.jdt.annotation.NonNullByDefault; -import org.eclipse.jdt.annotation.Nullable; -import org.openhab.core.OpenHAB; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This is a simple file based cache implementation. - * - * @author Christoph Weitkamp - Initial contribution - */ -@NonNullByDefault -public class ByteArrayFileCache { - - private final Logger logger = LoggerFactory.getLogger(ByteArrayFileCache.class); - - private static final String CACHE_FOLDER_NAME = "cache"; - public static final char EXTENSION_SEPARATOR = '.'; - - protected final File cacheFolder; - - public ByteArrayFileCache(String servicePID) { - // TODO track and limit folder size - // TODO support user specific folder - cacheFolder = new File(new File(new File(OpenHAB.getUserDataFolder()), CACHE_FOLDER_NAME), servicePID); - if (!cacheFolder.exists()) { - logger.debug("Creating cache folder '{}'", cacheFolder.getAbsolutePath()); - cacheFolder.mkdirs(); - } - logger.debug("Using cache folder '{}'", cacheFolder.getAbsolutePath()); - } - - /** - * Adds a file to the cache. If the cache previously contained a file for the key, the old file is replaced by the - * new content. - * - * @param key the key with which the file is to be associated - * @param content the content for the file to be associated with the specified key - */ - public void put(String key, byte[] content) { - writeFile(getUniqueFile(key), content); - } - - /** - * Adds a file to the cache. - * - * @param key the key with which the file is to be associated - * @param content the content for the file to be associated with the specified key - */ - public void putIfAbsent(String key, byte[] content) { - File fileInCache = getUniqueFile(key); - if (fileInCache.exists()) { - logger.debug("File '{}' present in cache", fileInCache.getName()); - } else { - writeFile(fileInCache, content); - } - } - - /** - * Adds a file to the cache and returns the content of the file. - * - * @param key the key with which the file is to be associated - * @param content the content for the file to be associated with the specified key - * @return the content of the file associated with the given key - */ - public byte[] putIfAbsentAndGet(String key, byte[] content) { - putIfAbsent(key, content); - - // return get(key); - return content; - } - - /** - * Writes the given content to the given {@link File}. - * - * @param fileInCache the {@link File} - * @param content the content to be written - */ - private void writeFile(File fileInCache, byte[] content) { - logger.debug("Caching file '{}'", fileInCache.getName()); - try { - Files.write(fileInCache.toPath(), content); - } catch (IOException e) { - logger.warn("Could not write file '{}' to cache", fileInCache.getName(), e); - } - } - - /** - * Checks if the key is present in the cache. - * - * @param key the key whose presence in the cache is to be tested - * @return true if the cache contains a file for the specified key - */ - public boolean containsKey(String key) { - return getUniqueFile(key).exists(); - } - - /** - * Removes the file associated with the given key from the cache. - * - * @param key the key whose associated file is to be removed - */ - public void remove(String key) { - deleteFile(getUniqueFile(key)); - } - - /** - * Deletes the given {@link File}. - * - * @param fileInCache the {@link File} - */ - private void deleteFile(File fileInCache) { - if (fileInCache.exists()) { - logger.debug("Deleting file '{}' from cache", fileInCache.getName()); - fileInCache.delete(); - } else { - logger.debug("File '{}' not found in cache", fileInCache.getName()); - } - } - - /** - * Removes all files from the cache. - */ - public void clear() { - File[] filesInCache = cacheFolder.listFiles(); - if (filesInCache != null && filesInCache.length > 0) { - logger.debug("Deleting all files from cache"); - Arrays.stream(filesInCache).forEach(File::delete); - } - } - - /** - * Returns the content of the file associated with the given key, if it is present. - * - * @param key the key whose associated file is to be returned - * @return the content of the file associated with the given key - */ - public byte[] get(String key) { - return readFile(getUniqueFile(key)); - } - - /** - * Reads the content from the given {@link File}, if it is present. - * - * @param fileInCache the {@link File} - * @return the content of the file - */ - private byte[] readFile(File fileInCache) { - if (fileInCache.exists()) { - logger.debug("Reading file '{}' from cache", fileInCache.getName()); - try { - return Files.readAllBytes(fileInCache.toPath()); - } catch (IOException e) { - logger.warn("Could not read file '{}' from cache", fileInCache.getName(), e); - } - } else { - logger.debug("File '{}' not found in cache", fileInCache.getName()); - } - return new byte[0]; - } - - /** - * Creates a unique {@link File} from the key with which the file is to be associated. - * - * @param key the key with which the file is to be associated - * @return unique file for the file associated with the given key - */ - private File getUniqueFile(String key) { - // TODO: store / cache file internally for faster operations - String fileExtension = getFileExtension(key); - return new File(cacheFolder, - getUniqueFileName(key) + (fileExtension == null ? "" : EXTENSION_SEPARATOR + fileExtension)); - } - - /** - * Gets the extension of a file name. - * - * @param fileName the file name to retrieve the extension of - * @return the extension of the file or null if none exists - */ - private @Nullable String getFileExtension(String fileName) { - int index = fileName.lastIndexOf(EXTENSION_SEPARATOR); - // exclude file names starting with a dot - if (index > 0) { - return fileName.substring(index + 1); - } else { - return null; - } - } - - /** - * Creates a unique file name from the key with which the file is to be associated. - * - * @param key the key with which the file is to be associated - * @return unique file name for the file associated with the given key - */ - private String getUniqueFileName(String key) { - try { - byte[] bytesOfFileName = key.getBytes(StandardCharsets.UTF_8); - MessageDigest md = MessageDigest.getInstance("MD5"); - byte[] md5Hash = md.digest(bytesOfFileName); - BigInteger bigInt = new BigInteger(1, md5Hash); - StringBuilder fileNameHash = new StringBuilder(bigInt.toString(16)); - // Now we need to zero pad it if you actually want the full 32 chars - while (fileNameHash.length() < 32) { - fileNameHash.insert(0, "0"); - } - return fileNameHash.toString(); - } catch (NoSuchAlgorithmException ex) { - // should not happen - logger.error("Could not create MD5 hash for key '{}'", key, ex); - return key.toString(); - } - } -}