UpdateOperationWithCacheFileTask.java
- /*******************************************************************************
- * Copyright 2013 André Rouél
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- ******************************************************************************/
- package net.sf.uadetector.datastore;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.net.URL;
- import java.nio.charset.Charset;
- import javax.annotation.Nonnull;
- import net.sf.qualitycheck.Check;
- import net.sf.uadetector.exception.CanNotOpenStreamException;
- import net.sf.uadetector.internal.data.Data;
- import net.sf.uadetector.internal.util.Closeables;
- import net.sf.uadetector.internal.util.FileUtil;
- import net.sf.uadetector.internal.util.UrlUtil;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- final class UpdateOperationWithCacheFileTask extends AbstractUpdateOperation {
- /**
- * Corresponding default logger of this class
- */
- private static final Logger LOG = LoggerFactory.getLogger(UpdateOperationWithCacheFileTask.class);
- /**
- * Message for the log when issues occur during reading of or writing to the cache file.
- */
- private static final String MSG_CACHE_FILE_ISSUES = "Issues occured during reading of or writing to the cache file: %s";
- /**
- * Message for the log if the passed resources are the same and an update makes no sense
- */
- private static final String MSG_SAME_RESOURCES = "The passed URL and file resources are the same. An update was not performed.";
- /**
- * Creates a temporary file near the passed file. The name of the given one will be used and the suffix ".temp" will
- * be added.
- *
- * @param file
- * file in which the entire contents from the given URL can be saved
- * @throws IllegalStateException
- * if the file can not be deleted
- */
- protected static File createTemporaryFile(@Nonnull final File file) {
- Check.notNull(file, "file");
- final File tempFile = new File(file.getParent(), file.getName() + ".temp");
- // remove orphaned temporary file
- deleteFile(tempFile);
- return tempFile;
- }
- /**
- * Removes the given file.
- *
- * @param file
- * a file which should be deleted
- *
- * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException
- * if the given argument is {@code null}
- * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException
- * if the file can not be deleted
- */
- protected static void deleteFile(@Nonnull final File file) {
- Check.notNull(file, "file");
- Check.stateIsTrue(!file.exists() || file.delete(), "Cannot delete file '%s'.", file.getPath());
- }
- /**
- * Checks if the given file is empty.
- *
- * @param file
- * the file that could be empty
- * @return {@code true} when the file is accessible and empty otherwise {@code false}
- * @throws IllegalStateException
- * if an I/O error occurs
- */
- private static boolean isEmpty(@Nonnull final File file, @Nonnull final Charset charset) {
- try {
- return FileUtil.isEmpty(file, charset);
- } catch (final IOException e) {
- throw new IllegalStateException("The given file could not be read.");
- }
- }
- /**
- * Checks that {@code older} {@link Data} has a lower version number than the {@code newer} one.
- *
- * @param older
- * possibly older {@code Data}
- * @param newer
- * possibly newer {@code Data}
- * @return {@code true} if the {@code newer} Data is really newer, otherwise {@code false}
- */
- protected static boolean isNewerData(@Nonnull final Data older, @Nonnull final Data newer) {
- return newer.getVersion().compareTo(older.getVersion()) > 0;
- }
- /**
- * Reads the content from the given {@link URL} and saves it to the passed file.
- *
- * @param file
- * file in which the entire contents from the given URL can be saved
- * @param store
- * a data store for <em>UAS data</em>
- * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException
- * if any of the passed arguments is {@code null}
- * @throws IOException
- * if an I/O error occurs
- */
- protected static void readAndSave(@Nonnull final File file, @Nonnull final DataStore store) throws IOException {
- Check.notNull(file, "file");
- Check.notNull(store, "store");
- final URL url = store.getDataUrl();
- final Charset charset = store.getCharset();
- final boolean isEqual = url.toExternalForm().equals(UrlUtil.toUrl(file).toExternalForm());
- if (!isEqual) {
- // check if the data can be read in successfully
- final String data = UrlUtil.read(url, charset);
- if (Data.EMPTY.equals(store.getDataReader().read(data))) {
- throw new IllegalStateException("The read in content can not be transformed to an instance of 'Data'.");
- }
- final File tempFile = createTemporaryFile(file);
- FileOutputStream outputStream = null;
- boolean threw = true;
- try {
- // write data to temporary file
- outputStream = new FileOutputStream(tempFile);
- outputStream.write(data.getBytes(charset));
- // delete the original file
- deleteFile(file);
- threw = false;
- } finally {
- Closeables.close(outputStream, threw);
- }
- // rename the new file to the original one
- renameFile(tempFile, file);
- } else {
- LOG.debug(MSG_SAME_RESOURCES);
- }
- }
- /**
- * Renames the given file {@code from} to the new file {@code to}.
- *
- * @param from
- * an existing file
- * @param to
- * a new file
- *
- * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException
- * if one of the given arguments is {@code null}
- * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException
- * if the file can not be renamed
- */
- protected static void renameFile(@Nonnull final File from, @Nonnull final File to) {
- Check.notNull(from, "from");
- Check.stateIsTrue(from.exists(), "Argument 'from' must not be an existing file.");
- Check.notNull(to, "to");
- Check.stateIsTrue(from.renameTo(to), "Renaming file from '%s' to '%s' failed.", from.getAbsolutePath(), to.getAbsolutePath());
- }
- /**
- * File to cache read in <em>UAS data</em>
- */
- private final File cacheFile;
- /**
- * The data store for instances that implements {@link net.sf.uadetector.internal.data.Data}
- */
- private final AbstractRefreshableDataStore store;
- public UpdateOperationWithCacheFileTask(@Nonnull final AbstractRefreshableDataStore dataStore, @Nonnull final File cacheFile) {
- super(dataStore);
- Check.notNull(dataStore, "dataStore");
- Check.notNull(cacheFile, "cacheFile");
- store = dataStore;
- this.cacheFile = cacheFile;
- }
- @Override
- public void call() {
- readDataIfNewerAvailable();
- }
- private boolean isCacheFileEmpty() {
- return isEmpty(cacheFile, store.getCharset());
- }
- private void readDataIfNewerAvailable() {
- try {
- if (isUpdateAvailable() || isCacheFileEmpty()) {
- readAndSave(cacheFile, store);
- store.setData(store.getDataReader().read(cacheFile.toURI().toURL(), store.getCharset()));
- }
- } catch (final CanNotOpenStreamException e) {
- LOG.warn(String.format(RefreshableDataStore.MSG_URL_NOT_READABLE, e.getLocalizedMessage()));
- readFallbackData();
- } catch (final RuntimeException e) {
- LOG.warn(RefreshableDataStore.MSG_FAULTY_CONTENT, e);
- readFallbackData();
- } catch (final IOException e) {
- LOG.warn(String.format(MSG_CACHE_FILE_ISSUES, e.getLocalizedMessage()), e);
- readFallbackData();
- }
- }
- private void readFallbackData() {
- LOG.info("Reading fallback data...");
- try {
- if (isCacheFileEmpty()) {
- readAndSave(cacheFile, store.getFallback());
- final Data data = store.getDataReader().read(cacheFile.toURI().toURL(), store.getCharset());
- if (isNewerData(store.getData(), data)) {
- store.setData(data);
- }
- }
- } catch (final CanNotOpenStreamException e) {
- LOG.warn(String.format(RefreshableDataStore.MSG_URL_NOT_READABLE, e.getLocalizedMessage()));
- } catch (final RuntimeException e) {
- LOG.warn(RefreshableDataStore.MSG_FAULTY_CONTENT, e);
- } catch (final IOException e) {
- LOG.warn(String.format(MSG_CACHE_FILE_ISSUES, e.getLocalizedMessage()), e);
- }
- }
- }