Package net.sf.uadetector.datastore

Source Code of net.sf.uadetector.datastore.UpdateOperationWithCacheFileTask

/*******************************************************************************
* 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);
    }
  }

}
TOP

Related Classes of net.sf.uadetector.datastore.UpdateOperationWithCacheFileTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.