All Downloads are FREE. Search and download functionalities are using the official Maven repository.

studio.raptor.ddal.config.reloading.FileHandlerReloadingDetector Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 studio.raptor.ddal.config.reloading;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import studio.raptor.ddal.config.io.FileHandler;
import studio.raptor.ddal.config.io.FileLocatorUtils;

/**
 * 基于文件的重载侦听
 *
 * @author Sam
 * @since 3.0.0
 */
public class FileHandlerReloadingDetector implements ReloadingDetector {

  /**
   * Constant for the jar URL protocol.
   */
  private static final String JAR_PROTOCOL = "jar";

  /**
   * Constant for the default refresh delay.
   */
  private static final int DEFAULT_REFRESH_DELAY = 5000;

  /**
   * The associated file handler.
   */
  private final FileHandler fileHandler;

  /**
   * The refresh delay.
   */
  private final long refreshDelay;

  /**
   * The last time the configuration file was modified.
   */
  private long lastModified;

  /**
   * The last time the file was checked for changes.
   */
  private long lastChecked;

  /**
   * Creates a new instance of {@code FileHandlerReloadingDetector} and
   * initializes it with the {@code FileHandler} to monitor and the refresh
   * delay. The handler is directly used, no copy is created. So it is
   * possible to change the location monitored by manipulating the
   * {@code FileHandler} object.
   *
   * @param handler the {@code FileHandler} associated with this detector (can be null)
   * @param refreshDelay the refresh delay; a value of 0 means that a check is performed in all
   * cases
   */
  public FileHandlerReloadingDetector(FileHandler handler, long refreshDelay) {
    fileHandler = (handler != null) ? handler : new FileHandler(null);
    this.refreshDelay = refreshDelay;
  }

  /**
   * Creates a new instance of {@code FileHandlerReloadingDetector} and
   * initializes it with the {@code FileHandler} to monitor and a default
   * refresh delay.
   *
   * @param handler the {@code FileHandler} associated with this detector (can be null)
   */
  public FileHandlerReloadingDetector(FileHandler handler) {
    this(handler, DEFAULT_REFRESH_DELAY);
  }

  /**
   * Creates a new instance of {@code FileHandlerReloadingDetector} with an
   * uninitialized {@code FileHandler} object. The file to be monitored has to
   * be set later by manipulating the handler object returned by
   * {@code getFileHandler()}.
   */
  public FileHandlerReloadingDetector() {
    this(null);
  }

  /**
   * Helper method for transforming a URL into a file object. This method
   * handles file: and jar: URLs.
   *
   * @param url the URL to be converted
   * @return the resulting file or null 
   */
  private static File fileFromURL(URL url) {
    if (JAR_PROTOCOL.equals(url.getProtocol())) {
      String path = url.getPath();
      try {
        return FileLocatorUtils.fileFromURL(new URL(path.substring(0, path.indexOf('!'))));
      } catch (MalformedURLException mex) {
        return null;
      }
    } else {
      return FileLocatorUtils.fileFromURL(url);
    }
  }

  /**
   * Returns the {@code FileHandler} associated with this object. The
   * underlying handler is directly returned, so changing its location also
   * changes the file monitored by this detector.
   *
   * @return the associated {@code FileHandler}
   */
  public FileHandler getFileHandler() {
    return fileHandler;
  }

  /**
   * Returns the refresh delay. This is a time in milliseconds. The
   * {@code isReloadingRequired()} method first checks whether the time since
   * the previous check is more than this value in the past. Otherwise, no
   * check is performed. This is a means to limit file I/O caused by this
   * class.
   *
   * @return the refresh delay used by this object
   */
  public long getRefreshDelay() {
    return refreshDelay;
  }

  /**
   * {@inheritDoc} This implementation checks whether the associated
   * {@link FileHandler} points to a valid file and whether the last
   * modification time of this time has changed since the last check. The
   * refresh delay is taken into account, too; a check is only performed if at
   * least this time has passed since the last check.
   */
  @Override
  public boolean isReloadingRequired() {
    long now = System.currentTimeMillis();
    if (now >= lastChecked + getRefreshDelay()) {
      lastChecked = now;

      long modified = getLastModificationDate();
      if (modified > 0) {
        if (lastModified == 0) {
          // initialization
          updateLastModified(modified);
        } else {
          if (modified != lastModified) {
            return true;
          }
        }
      }
    }
    return false;
  }

  /**
   * {@inheritDoc} This implementation updates the internally stored last
   * modification date with the current modification date of the monitored
   * file. So the next change is detected when this file is changed again.
   */
  @Override
  public void reloadingPerformed() {
    updateLastModified(getLastModificationDate());
  }

  /**
   * Returns the date of the last modification of the monitored file. A return
   * value of 0 indicates, that the monitored file does not exist.
   *
   * @return the last modification date
   */
  protected long getLastModificationDate() {
    File file = getExistingFile();
    return (file != null) ? file.lastModified() : 0;
  }

  /**
   * Updates the last modification date of the monitored file. The need for a
   * reload is detected only if the file's modification date is different from
   * this value.
   *
   * @param time the new last modification date
   */
  protected void updateLastModified(long time) {
    lastModified = time;
  }

  /**
   * Returns the {@code File} object which is monitored by this object. This
   * method is called every time the file's last modification time is needed.
   * If it returns null, no check is performed. This base
   * implementation obtains the {@code File} from the associated
   * {@code FileHandler}. It can also deal with URLs to jar files.
   *
   * @return the {@code File} to be monitored (can be null)
   */
  protected File getFile() {
    URL url = getFileHandler().getURL();
    return (url != null) ? fileFromURL(url) : getFileHandler().getFile();
  }

  /**
   * Returns the monitored {@code File} or null if it does not exist.
   *
   * @return the monitored {@code File} or null
   */
  private File getExistingFile() {
    File file = getFile();
    if (file != null && !file.exists()) {
      file = null;
    }
    return file;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy