You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by db...@apache.org on 2007/01/26 09:46:28 UTC
svn commit: r500182 -
/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/DirectoryMonitor.java
Author: dblevins
Date: Fri Jan 26 00:46:27 2007
New Revision: 500182
URL: http://svn.apache.org/viewvc?view=rev&rev=500182
Log:
Basic directory monitor
Added:
incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/DirectoryMonitor.java
Added: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/DirectoryMonitor.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/DirectoryMonitor.java?view=auto&rev=500182
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/DirectoryMonitor.java (added)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/DirectoryMonitor.java Fri Jan 26 00:46:27 2007
@@ -0,0 +1,321 @@
+/**
+ * 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 org.apache.openejb.util;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class DirectoryMonitor {
+
+ public static final Logger logger = Logger.getInstance("OpenEJB.deploy", DirectoryMonitor.class.getPackage().getName());
+
+ private boolean run;
+
+ private int pollIntervalMillis;
+
+ private File directory;
+
+ private Listener listener;
+
+ private Map files = new HashMap();
+
+ public DirectoryMonitor(final File directory, final Listener listener, final int pollIntervalMillis, final Logger logger) {
+ assert listener == null : "No listener specified";
+ assert directory.isDirectory() : "File specified is not a directory. " + directory.getAbsolutePath();
+ assert directory.canRead() : "Directory specified cannot be read. " + directory.getAbsolutePath();
+ assert pollIntervalMillis > 0 : "Poll Interval must be above zero.";
+
+ this.directory = directory;
+ this.listener = listener;
+ this.pollIntervalMillis = pollIntervalMillis;
+ }
+
+ public Logger getLogger() {
+ return logger;
+ }
+
+ public int getPollIntervalMillis() {
+ return pollIntervalMillis;
+ }
+
+ public File getDirectory() {
+ return directory;
+ }
+
+ public Listener getListener() {
+ return listener;
+ }
+
+ public synchronized boolean isRunning() {
+ return run;
+ }
+
+ public synchronized void stop() {
+ this.run = false;
+ }
+
+ public void run() {
+ run = true;
+ initialize();
+
+ getLogger().debug("Scanner running. Polling every " + pollIntervalMillis + " milliseconds.");
+
+ while (run) {
+ try {
+ scanDirectory();
+ }
+ catch (Exception e) {
+ getLogger().error("Scan failed.", e);
+ }
+
+ try {
+ Thread.sleep(pollIntervalMillis);
+ }
+ catch (InterruptedException ignore) {
+ // empty
+ }
+ }
+ }
+
+ public void initialize() {
+ getLogger().debug("Doing initial scan of " + directory.getAbsolutePath());
+
+ File parent = directory;
+ File[] children = parent.listFiles();
+
+ for (int i = 0; children != null && i < children.length; i++) {
+ File child = children[i];
+
+ if (!child.canRead()) {
+ continue;
+ }
+
+ FileInfo now = newInfo(child);
+ now.setChanging(false);
+ }
+ }
+
+ private FileInfo newInfo(File child) {
+ FileInfo fileInfo = child.isDirectory() ? new DirectoryInfo(child) : new FileInfo(child);
+ files.put(fileInfo.getPath(), fileInfo);
+ return fileInfo;
+ }
+
+ /**
+ * Looks for changes to the immediate contents of the directory we're watching.
+ */
+ public void scanDirectory() {
+ File parent = directory;
+ File[] children = parent.listFiles();
+
+ HashSet<String> missingFilesList = new HashSet(files.keySet());
+
+ for (int i = 0; children != null && i < children.length; i++) {
+ File child = children[i];
+
+ missingFilesList.remove(child.getAbsolutePath());
+
+ if (!child.canRead()) {
+ getLogger().debug("not readable " + child.getName());
+ continue;
+ }
+
+ FileInfo oldStatus = oldInfo(child);
+ FileInfo newStatus = newInfo(child);
+
+ newStatus.diff(oldStatus);
+
+ if (oldStatus == null) {
+ // Brand new, but assume it's changing and
+ // wait a bit to make sure it's not still changing
+ getLogger().debug("File Discovered: " + newStatus);
+ } else if (newStatus.isChanging()) {
+ // The two records are different -- record the latest as a file that's changing
+ // and later when it stops changing we'll do the add or update as appropriate.
+ getLogger().debug("File Changing: " + newStatus);
+ } else if (oldStatus.isNewFile()) {
+ // Used to be changing, now in (hopefully) its final state
+ getLogger().info("New File: " + newStatus);
+ newStatus.setNewFile(!listener.fileAdded(child));
+ } else if (oldStatus.isChanging()) {
+ getLogger().info("Updated File: " + newStatus);
+ listener.fileUpdated(child);
+
+ missingFilesList.remove(oldStatus.getPath());
+ }
+ // else it's just totally unchanged and we ignore it this pass
+ }
+
+ // Look for any files we used to know about but didn't find in this pass
+ for (String path : missingFilesList) {
+ getLogger().info("File removed: " + path);
+
+ if (listener.fileRemoved(new File(path))) {
+ files.remove(path);
+ }
+ }
+ }
+
+ private FileInfo oldInfo(File file) {
+ return (FileInfo) files.get(file.getAbsolutePath());
+ }
+
+ /**
+ * Allows custom behavior to be hooked up to process file state changes.
+ */
+ public interface Listener {
+ boolean fileAdded(File file);
+
+ boolean fileRemoved(File file);
+
+ void fileUpdated(File file);
+ }
+
+ /**
+ * Provides details about a directory.
+ */
+ private static class DirectoryInfo extends FileInfo {
+ public DirectoryInfo(final File dir) {
+ //
+ // We don't pay attention to the size of the directory or files in the
+ // directory, only the highest last modified time of anything in the
+ // directory. Hopefully this is good enough.
+ //
+ super(dir.getAbsolutePath(), 0, getLastModifiedInDir(dir));
+ }
+
+ private static long getLastModifiedInDir(final File dir) {
+ assert dir != null;
+
+ long value = dir.lastModified();
+ File[] children = dir.listFiles();
+ long test;
+
+ for (int i = 0; i < children.length; i++) {
+ File child = children[i];
+
+ if (!child.canRead()) {
+ continue;
+ }
+
+ if (child.isDirectory()) {
+ test = getLastModifiedInDir(child);
+ } else {
+ test = child.lastModified();
+ }
+
+ if (test > value) {
+ value = test;
+ }
+ }
+
+ return value;
+ }
+ }
+
+ /**
+ * Provides details about a file.
+ */
+ private static class FileInfo implements Serializable {
+ private String path;
+
+ private long size;
+
+ private long modified;
+
+ private boolean newFile;
+
+ private boolean changing;
+
+ public FileInfo(final File file) {
+ this(file.getAbsolutePath(), file.length(), file.lastModified());
+ }
+
+ public FileInfo(final String path, final long size, final long modified) {
+ assert path != null;
+
+ this.path = path;
+ this.size = size;
+ this.modified = modified;
+ this.newFile = true;
+ this.changing = true;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public void setSize(final long size) {
+ this.size = size;
+ }
+
+ public long getModified() {
+ return modified;
+ }
+
+ public void setModified(final long modified) {
+ this.modified = modified;
+ }
+
+ public boolean isNewFile() {
+ return newFile;
+ }
+
+ public void setNewFile(final boolean newFile) {
+ this.newFile = newFile;
+ }
+
+ public boolean isChanging() {
+ return changing;
+ }
+
+ public void setChanging(final boolean changing) {
+ this.changing = changing;
+ }
+
+ public boolean isSame(final FileInfo info) {
+ assert info != null;
+
+ if (!path.equals(info.path)) {
+ throw new IllegalArgumentException("Should only be used to compare two files representing the same path!");
+ }
+
+ return size == info.size && modified == info.modified;
+ }
+
+ public String toString() {
+ return path;
+ }
+
+ public void diff(final FileInfo old) {
+ if (old != null) {
+ this.changing = !isSame(old);
+ this.newFile = old.newFile;
+ }
+ }
+ }
+}