Package net.sourceforge.javautil.common.io

Source Code of net.sourceforge.javautil.common.io.VirtualArtifactWatcher$VirtualArtifactWatchable

package net.sourceforge.javautil.common.io;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.regex.Pattern;

import net.sourceforge.javautil.common.ThreadUtil;
import net.sourceforge.javautil.common.api.IWrapper;
import net.sourceforge.javautil.common.context.Context;
import net.sourceforge.javautil.common.io.IVirtualArtifactWatcherListener.VirtualArtifactWatcherEvent;
import net.sourceforge.javautil.common.io.IVirtualArtifactWatcherListener.VirtualArtifactWatcherEvent.Type;
import net.sourceforge.javautil.common.proxy.CollectionTargetProxy;
import net.sourceforge.javautil.common.shutdown.IShutdownHook;

/**
* A service that will watch for changes to specified {@link IVirtualArtifact}'s and can be ran inside a thread.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class VirtualArtifactWatcher extends Context implements Runnable, IShutdownHook {
 
  /**
   * @return The current watcher for this thread, or null if none have been set
   */
  public static VirtualArtifactWatcher getInstance () { return Context.get(VirtualArtifactWatcher.class); }
 
  protected boolean running = false;
 
  protected int cycleSleep = 2000;
  protected int interSleep = 20;
  protected long last = -1;
 
  protected final String name;
 
  protected final Map<String, VirtualArtifactWatchable> artifacts = Collections.synchronizedMap(new LinkedHashMap<String, VirtualArtifactWatchable>());
 
  protected final Set<IVirtualArtifactWatcherListener> listeners = new CopyOnWriteArraySet<IVirtualArtifactWatcherListener>();
  protected final IVirtualArtifactWatcherListener propogator = CollectionTargetProxy.create(
    Thread.currentThread().getContextClassLoader(), IVirtualArtifactWatcherListener.class, listeners, true
  );
 
  public VirtualArtifactWatcher(String name) { this.name = name; }
 
  /**
   * @param listener The listener to recieve filtered events
   * @param paths The paths to be matched on and for which events will be propagated for the listener
   */
  public void addListener (IVirtualArtifactWatcherListener listener, IVirtualPath... paths) {
    this.listeners.add(new FilteredWatcher(listener, paths));
  }
 
  /**
   * @param listener The listener to receive filtered events
   * @param pattern The pattern to filter which events will be propagated to the listener
   */
  public void addListener (IVirtualArtifactWatcherListener listener, String pattern) {
    this.listeners.add(new PatternWatcher(Pattern.compile(pattern), listener));
  }

  /**
   * @param listener The listener who will receive {@link VirtualArtifactWatcherEvent}'s from this watcher
   */
  public void addListener (IVirtualArtifactWatcherListener listener) {
    this.listeners.add(listener);
  }
 
  /**
   * @param listener The listener who will no longer receive {@link VirtualArtifactWatcherEvent}'s from this watcher
   */
  public void removeListener (IVirtualArtifactWatcherListener listener) {
    if (!this.listeners.remove(listener)) {
      IVirtualArtifactWatcherListener wrapper = null;
      for (IVirtualArtifactWatcherListener l : listeners) {
        if (l instanceof IWrapper && ((IVirtualArtifactWatcherListener)((IWrapper)l).getWrapped()) == listener) {
          wrapper = l; break;
        }
      }
      if (wrapper != null) this.removeListener(wrapper);
    }
  }
 
  /**
   * @param watchable The watchable to add to the list of watched artifacts
   */
  public synchronized void watch (VirtualArtifactWatchable watchable) { this.artifacts.put(watchable.getArtifact().getPath().toString("/"), watchable); }
 
  /**
   * @param directory The directory to watch
   * @param recursive True if all the artifacts recursively should be watched, or if just the artifacts in the root of the directory
   */
  public synchronized void watch (IVirtualDirectory directory, boolean recursive) {
    this.watch(new VirtualDirectoryWatched(directory, recursive));
  }
 
  /**
   * @param artifact The artifact to stop watching
   */
  public synchronized void stopWatching (VirtualArtifactWatchable watched) {
    this.artifacts.remove(watched.getArtifact().getPath().toString("/"));
  }

  /**
   * The higher this setting is the slower the watcher will be in responding to events.
   *
   * @return The time in milliseconds that the watcher will sleep between full cycle checks on the registered artifacts
   */
  public int getCycleSleep() { return cycleSleep; }
  public void setCycleSleep(int cycleSleep) { this.cycleSleep = cycleSleep; }

  /**
   * @return The time in milliseconds that the watcher will sleep between each {@link IVirtualArtifact} check
   */
  public int getInterSleep() { return interSleep; }
  public void setInterSleep(int interSleep) { this.interSleep = interSleep; }

  public void run() {
    this.running = true;
    this.last = System.currentTimeMillis();
    while (this.running) {
      this.check(interSleep);
      ThreadUtil.sleep(cycleSleep);
    }
  }
 
  /**
   * This will do a single check on the resources.
   *
   * @param changes The array to utilize for storing changes
   * @param interSleep The time in milliseconds to sleep between each artifact check
   */
  public void check (int interSleep) {
    List<VirtualArtifactChange> changes = null;
    for (String path : this.artifacts.keySet()) {
      changes = this.artifacts.get(path).getArtifactChangeList(this.last);
      if (changes != null) {
        for (int c=0; c<changes.size(); c++) {
          VirtualArtifactChange change = changes.get(c);
          if (change.getTimestamp() > last) last = change.getTimestamp();
          this.propogator.handle(new VirtualArtifactWatcherEvent(this, change));
        }
      }
      ThreadUtil.sleep(interSleep);
    }
  }
 
  /**
   * Start the watcher in another thread
   */
  public Thread start () {
    if (this.running) throw new IllegalStateException("This watcher is already running");
    Thread thread = new Thread(this, "Watcher - " + name);
    thread.setDaemon(true);
    thread.start();
    return thread;
  }
 
  /**
   * Stop the watcher from running
   */
  public void stop () {
    if (!this.running) throw new IllegalStateException("This watcher is not running");
    this.running = false;
  }

  public void shutdown() { if (this.running) this.stop(); }
 
  /**
   * This will allow a list of {@link IVirtualPath}'s to be alerted about.
   *
   * @author elponderador
   * @author $Author$
   * @version $Id$
   */
  public class FilteredWatcher implements IVirtualArtifactWatcherListener, IWrapper<IVirtualArtifactWatcherListener> {
   
    protected final Set<IVirtualPath> paths;
    protected final IVirtualArtifactWatcherListener original;
   
    public FilteredWatcher(IVirtualArtifactWatcherListener original, IVirtualPath... paths) {
      this.paths = new LinkedHashSet<IVirtualPath>();
      Collections.addAll(this.paths, paths);
      this.original = original;
    }

    public IVirtualArtifactWatcherListener getWrapped() {
      return this.original;
    }

    public void handle(VirtualArtifactWatcherEvent evt) {
      if (paths.contains(evt.getChange().getPath())) {
        this.original.handle(evt);
      }
    }
   
  }
 
  /**
   * An implementation using a {@link Pattern} to filter event's handled by a target listener.
   *
   * @author elponderador
   * @author $Author$
   * @version $Id$
   */
  public class PatternWatcher implements IVirtualArtifactWatcherListener, IWrapper<IVirtualArtifactWatcherListener> {
   
    protected final Pattern pattern;
    protected final IVirtualArtifactWatcherListener original;
   
    public PatternWatcher(Pattern pattern, IVirtualArtifactWatcherListener original) {
      this.pattern = pattern;
      this.original = original;
    }

    public void handle(VirtualArtifactWatcherEvent evt) {
      if (pattern.matcher(evt.getChange().getPath().toString("/")).matches()) {
        this.original.handle(evt);
      }
    }

    public IVirtualArtifactWatcherListener getWrapped() {
      return original;
    }
   
  }
 
  /**
   * The base interface for implementations that can generate a list of changed artifacts.
   *
   * @author elponderador
   * @author $Author$
   * @version $Id$
   */
  public static interface VirtualArtifactWatchable<A extends IVirtualArtifact> {
   
    /**
     * @return The artifact being watched
     */
    A getArtifact ();
   
    /**
     * @param since The time stamp from which changes should be detected
     *
     * @return A list of changes that have taken place since the time stamp or null if no changes have taken place
     */
    List<VirtualArtifactChange> getArtifactChangeList (long since);
   
  }
 
  /**
   * This represents a change to a {@link IVirtualArtifact}.
   *
   * @author elponderador
   * @author $Author$
   * @version $Id$
   */
  public static class VirtualArtifactChange {
   
    protected final IVirtualArtifact owner;
    protected final IVirtualPath path;
    protected final Type type;
    protected final long timestamp;
   
    public VirtualArtifactChange(IVirtualArtifact owner, IVirtualPath path, Type type, long timestamp) {
      this.owner = owner;
      this.path = path;
      this.type = type;
      this.timestamp = timestamp;
    }
   
    /**
     * @return A time stamp reflecting when the change took place if known, otherwise when it was detected
     */
    public long getTimestamp() { return timestamp; }

    /**
     * @return The artifact that generated the change, which may or may not be the same as referred to by the path
     */
    public IVirtualArtifact getOwner() { return owner; }
   
    /**
     * @return The path that is related to the change
     */
    public IVirtualPath getPath () { return path; }

    /**
     * @return The type of change
     */
    public Type getType() { return type; }
   
  }

}
TOP

Related Classes of net.sourceforge.javautil.common.io.VirtualArtifactWatcher$VirtualArtifactWatchable

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.