Package org.drools.io.impl

Source Code of org.drools.io.impl.ResourceChangeScannerImpl

package org.drools.io.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;

import org.drools.ChangeSet;
import org.drools.SystemEventListener;
import org.drools.SystemEventListenerFactory;
import org.drools.io.Resource;
import org.drools.io.ResourceChangeNotifier;
import org.drools.io.ResourceChangeScanner;
import org.drools.io.ResourceChangeScannerConfiguration;
import org.drools.io.internal.InternalResource;

public class ResourceChangeScannerImpl implements ResourceChangeScanner {

  private Map<Resource, Set<ResourceChangeNotifier>> resources;
  private Set<Resource> directories;
  private SystemEventListener listener;
  private int interval;

  public ResourceChangeScannerImpl() {
    this.listener = SystemEventListenerFactory.getSystemEventListener();
    this.resources = new HashMap<Resource, Set<ResourceChangeNotifier>>();
    this.directories = new HashSet<Resource>();
    this.interval = 60;
        this.listener.info( "ResourceChangeScanner created with default interval=60" );
  }

  public void setSystemEventListener(SystemEventListener listener) {
    this.listener = listener;
  }

  public void configure(ResourceChangeScannerConfiguration configuration) {
        this.interval = ((ResourceChangeScannerConfigurationImpl) configuration).getInterval();
        this.listener.info( "ResourceChangeScanner reconfigured with interval=" + getInterval() );

    // restart it if it's already running.
    if (this.scannerScheduler != null && this.scannerScheduler.isRunning()) {
      stop();
      start();
    }
  }

  public ResourceChangeScannerConfiguration newResourceChangeScannerConfiguration() {
    return new ResourceChangeScannerConfigurationImpl();
  }

  public ResourceChangeScannerConfiguration newResourceChangeScannerConfiguration(
      Properties properties) {
    return new ResourceChangeScannerConfigurationImpl(properties);
  }

  public void subscribeNotifier(ResourceChangeNotifier notifier,
      Resource resource) {
    synchronized (this.resources) {
      if (((InternalResource) resource).isDirectory()) {
        this.directories.add(resource);
      }
      Set<ResourceChangeNotifier> notifiers = this.resources
          .get(resource);
      if (notifiers == null) {
        notifiers = new HashSet<ResourceChangeNotifier>();
        this.resources.put(resource, notifiers);
      }
      this.listener.debug("ResourceChangeScanner subcribing notifier="
          + notifier + " to resource=" + resource);
      notifiers.add(notifier);
    }
  }

  public void unsubscribeNotifier(ResourceChangeNotifier notifier,
      Resource resource) {
    synchronized (this.resources) {
      Set<ResourceChangeNotifier> notifiers = this.resources
          .get(resource);
      if (notifiers == null) {
        return;
      }
      this.listener.debug("ResourceChangeScanner unsubcribing notifier="
          + notifier + " to resource=" + resource);
      notifiers.remove(notifier);
      if (notifiers.isEmpty()) {
        this.listener.debug("ResourceChangeScanner resource="
            + resource + " now has no subscribers");
        this.resources.remove(resource);
        this.directories.remove(resource); // don't bother with
        // isDirectory check, as
        // doing a remove is
        // harmless if it doesn't
        // exist
      }
    }
  }

  public void scan() {
    this.listener.debug("ResourceChangeScanner attempt to scan "
        + this.resources.size() + " resources");

    synchronized (this.resources) {
      Map<ResourceChangeNotifier, ChangeSet> notifications = new HashMap<ResourceChangeNotifier, ChangeSet>();

      List<Resource> removed = new ArrayList<Resource>();

      // detect modified and added
      for (Resource resource : this.directories) {
        this.listener.debug("ResourceChangeScanner scanning directory="
            + resource);
        for (Resource child : ((InternalResource) resource)
            .listResources()) {
          if (((InternalResource) child).isDirectory()) {
            continue; // ignore sub directories
          }
          if (!this.resources.containsKey(child)) {

            this.listener
                .debug("ResourceChangeScanner new resource="
                    + child);
            // child is new
            ((InternalResource) child)
                .setResourceType(((InternalResource) resource)
                    .getResourceType());
            Set<ResourceChangeNotifier> notifiers = this.resources
                .get(resource); // get notifiers for this
            // directory
            for (ResourceChangeNotifier notifier : notifiers) {
              ChangeSetImpl changeSet = (ChangeSetImpl) notifications
                  .get(notifier);
              if (changeSet == null) {
                // lazy initialise changeSet
                changeSet = new ChangeSetImpl();
                notifications.put(notifier, changeSet);
              }
              if (changeSet.getResourcesAdded().isEmpty()) {
                changeSet
                    .setResourcesAdded(new ArrayList<Resource>());
              }
              changeSet.getResourcesAdded().add(child);
              notifier.subscribeChildResource(resource, child);
            }
          }
        }
      }

      for (Entry<Resource, Set<ResourceChangeNotifier>> entry : this.resources
          .entrySet()) {
        Resource resource = entry.getKey();
        Set<ResourceChangeNotifier> notifiers = entry.getValue();

        if (!((InternalResource) resource).isDirectory()) {
          // detect if Resource has been removed
          long lastModified = ((InternalResource) resource)
              .getLastModified();
          long lastRead = ((InternalResource) resource).getLastRead();
          if (lastModified == 0) {
            this.listener
                .debug("ResourceChangeScanner removed resource="
                    + resource);
            removed.add(resource);
            // resource is no longer present
            // iterate notifiers for this resource and add to each
            // removed
            for (ResourceChangeNotifier notifier : notifiers) {
              ChangeSetImpl changeSet = (ChangeSetImpl) notifications
                  .get(notifier);
              if (changeSet == null) {
                // lazy initialise changeSet
                changeSet = new ChangeSetImpl();
                notifications.put(notifier, changeSet);
              }
              if (changeSet.getResourcesRemoved().isEmpty()) {
                changeSet
                    .setResourcesRemoved(new ArrayList<Resource>());
              }
              changeSet.getResourcesRemoved().add(resource);
            }
          } else if (lastRead < lastModified && lastRead >= 0) {
            this.listener
                .debug("ResourceChangeScanner modified resource="
                    + resource
                    + " : "
                    + lastRead
                    + " : "
                    + lastModified);
            // it's modified
            // iterate notifiers for this resource and add to each
            // modified
            for (ResourceChangeNotifier notifier : notifiers) {
              ChangeSetImpl changeSet = (ChangeSetImpl) notifications
                  .get(notifier);
              if (changeSet == null) {
                // lazy initialise changeSet
                changeSet = new ChangeSetImpl();
                notifications.put(notifier, changeSet);
              }
              if (changeSet.getResourcesModified().isEmpty()) {
                changeSet
                    .setResourcesModified(new ArrayList<Resource>());
              }
              changeSet.getResourcesModified().add(resource);
            }
          }
        }
      }

      // now iterate and removed the removed resources, we do this so as
      // not to mutate the foreach loop while iterating
      for (Resource resource : removed) {
        this.resources.remove(resource);
      }

      for (Entry<ResourceChangeNotifier, ChangeSet> entry : notifications
          .entrySet()) {
        ResourceChangeNotifier notifier = entry.getKey();
        ChangeSet changeSet = entry.getValue();
        notifier.publishChangeSet(changeSet);
      }
    }
  }

  public void setInterval(int interval) {
    this.interval = interval;
    this.listener.info("ResourceChangeScanner reconfigured with interval="
        + getInterval());

    if (this.scannerScheduler != null && this.scannerScheduler.isRunning()) {
      stop();
      start();
    }
  }

  public int getInterval() {
    return this.interval;
  }

  public void start() {
    this.scannerScheduler = new ProcessChangeSet(this.resources, this,
        this.listener, this.interval);
    thread = new Thread(this.scannerScheduler);
    thread.start();
  }

  public void stop() {
    if (this.scannerScheduler != null && this.scannerScheduler.isRunning()) {
      this.scannerScheduler.stop();
      this.thread.interrupt();
      this.scannerScheduler = null;
    }
  }

  public void reset() {
    this.resources.clear();
    this.directories.clear();
  }

  private Thread thread;
  private ProcessChangeSet scannerScheduler;

  public static class ProcessChangeSet implements Runnable {
    private volatile boolean scan;
    private ResourceChangeScannerImpl scanner;
    private long interval;
    private Map<Resource, Set<ResourceChangeNotifier>> resources;
    private SystemEventListener listener;

    ProcessChangeSet(Map<Resource, Set<ResourceChangeNotifier>> resources,
        ResourceChangeScannerImpl scanner,
        SystemEventListener listener, int interval) {
      this.resources = resources;
      this.scanner = scanner;
      this.listener = listener;
      this.interval = interval;
      this.scan = true;
    }

    public int getInterval() {
      return (int) this.interval;
    }

    public void stop() {
      this.scan = false;
    }

    public boolean isRunning() {
      return this.scan;
    }

    public void run() {
      synchronized (this) {
        if (this.scan) {
          this.listener
              .info("ResourceChangeNotification scanner has started");
        }
        while (this.scan) {
          Exception exception = null;
          // System.out.println( "BEFORE : sync this.resources" );
          synchronized (this.resources) {
            // System.out.println( "DURING : sync this.resources" );
            // lock the resources, as we don't want this modified
            // while processing
            this.scanner.scan();
          }
          // System.out.println( "AFTER : SCAN" );
          try {
            this.listener
                .debug("ResourceChangeScanner thread is waiting for "
                    + this.interval + " seconds.");
            wait(this.interval * 1000);
          } catch (InterruptedException e) {
            exception = e;
          }

          if (this.scan && exception != null) {
            this.listener
                .exception(new RuntimeException(
                    "ResourceChangeNotification ChangeSet scanning thread was interrupted, but shutdown was not requested",
                    exception));
          }
        }
        this.listener
            .info("ResourceChangeNotification scanner has stopped");
      }
    }
  }
}
TOP

Related Classes of org.drools.io.impl.ResourceChangeScannerImpl

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.