From 4640fd73a08f2233f79c8bb92e772fa58d3afb11 Mon Sep 17 00:00:00 2001
From: ribeaudc <ribeaudc>
Date: Wed, 15 Aug 2007 08:10:35 +0000
Subject: [PATCH] change: - If 'RoleBasedAccessController' is configured using
 a file, this file will be watched for changes. add: - 'FileWatcher' and
 'FileWatcherSynchronizer' in common remove: - TODO

SVN: 1368
---
 .../cisd/common/utilities/FileWatcher.java    | 167 ++++++++++++++++++
 .../utilities/FileWatcherSynchronizer.java    | 152 ++++++++++++++++
 2 files changed, 319 insertions(+)
 create mode 100644 common/source/java/ch/systemsx/cisd/common/utilities/FileWatcher.java
 create mode 100644 common/source/java/ch/systemsx/cisd/common/utilities/FileWatcherSynchronizer.java

diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/FileWatcher.java b/common/source/java/ch/systemsx/cisd/common/utilities/FileWatcher.java
new file mode 100644
index 00000000000..076d0af6c25
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/FileWatcher.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2007 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.common.utilities;
+
+import java.io.File;
+import java.util.TimerTask;
+
+import org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+
+/**
+ * A <code>TimerTask</code> extension that watch a given file.
+ * 
+ * @author Christian Ribeaud
+ */
+public abstract class FileWatcher extends TimerTask
+{
+
+    private static final Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, FileWatcher.class);
+
+    /** The default <code>FileWatcherState</code> implementation used. */
+    private static final IFileWatcherState DEFAULT_FILE_WATCHER_STATE = new LastModifiedState();
+
+    private final File fileToWatch;
+
+    private final IFileWatcherState fileWatcherState;
+
+    /** Whether the user has already been warned. */
+    private boolean warnedAlready = false;
+
+    public FileWatcher(File fileToWatch)
+    {
+        this(fileToWatch, DEFAULT_FILE_WATCHER_STATE);
+    }
+
+    public FileWatcher(File fileToWatch, IFileWatcherState fileWatcherState)
+    {
+        this.fileToWatch = fileToWatch;
+        this.fileWatcherState = fileWatcherState;
+        fileWatcherState.saveInitialState(fileToWatch);
+    }
+
+    public final File getFileToWatch()
+    {
+        return fileToWatch;
+    }
+
+    /** The action we should perform when the file we are watching has changed its state. */
+    protected abstract void onChange();
+
+    //
+    // TimerTask
+    //
+
+    @Override
+    public final void run()
+    {
+        boolean fileExists = false;
+        try
+        {
+            fileExists = fileToWatch.exists();
+        } catch (SecurityException e)
+        {
+            operationLog.warn(String.format("Was not allowed to check existence of file '%s'.", fileToWatch), e);
+            return;
+        }
+        if (fileExists)
+        {
+            if (fileWatcherState.stateChanged(fileToWatch))
+            {
+                if (operationLog.isDebugEnabled())
+                {
+                    operationLog.debug(String.format("Watched file '%s' has changed.", fileToWatch));
+                }
+                onChange();
+                warnedAlready = false;
+            } else
+            {
+                if (operationLog.isDebugEnabled())
+                {
+                    operationLog.debug(String.format("Watched file '%s' did not change.", fileToWatch));
+                }
+            }
+        } else
+        {
+            if (warnedAlready == false)
+            {
+                operationLog.warn(String.format("Given file '%s' does not exist.", fileToWatch));
+                warnedAlready = true;
+            }
+        }
+    }
+
+    /**
+     * Specifies the file property (or character) we are watching.
+     * 
+     * @author Christian Ribeaud
+     */
+    public static interface IFileWatcherState
+    {
+
+        /**
+         * Saves the initial state of given <var>fileToWatch</var>.
+         * <p>
+         * This method is called when <code>FileWatcher</code> gets instantiated.
+         * </p>
+         */
+        public void saveInitialState(File fileToWatch);
+
+        /**
+         * Whether the file state has changed.
+         * <p>
+         * If the file state has changed, each implementation should save the new state before returning
+         * <code>true</code>.
+         * </p>
+         */
+        public boolean stateChanged(File fileToWatch);
+    }
+
+    /**
+     * A <code>FileWatcherState</code> implementation that works with the value returned by
+     * {@link File#lastModified()}.
+     * 
+     * @author Christian Ribeaud
+     */
+    public final static class LastModifiedState implements IFileWatcherState
+    {
+
+        private long lastModified;
+
+        //
+        // FileWatcherCondition
+        //
+
+        public final void saveInitialState(File fileToWatch)
+        {
+            lastModified = fileToWatch.lastModified();
+        }
+
+        public final boolean stateChanged(File fileToWatch)
+        {
+            long current = fileToWatch.lastModified();
+            if (current > lastModified)
+            {
+                lastModified = current;
+                return true;
+            }
+            return false;
+        }
+    }
+}
diff --git a/common/source/java/ch/systemsx/cisd/common/utilities/FileWatcherSynchronizer.java b/common/source/java/ch/systemsx/cisd/common/utilities/FileWatcherSynchronizer.java
new file mode 100644
index 00000000000..3273aed3a3c
--- /dev/null
+++ b/common/source/java/ch/systemsx/cisd/common/utilities/FileWatcherSynchronizer.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2007 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.common.utilities;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TimerTask;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import org.apache.log4j.Logger;
+
+import ch.systemsx.cisd.common.logging.LogCategory;
+import ch.systemsx.cisd.common.logging.LogFactory;
+
+/**
+ * Kind of <code>FileWatcher</code> extension that allow registration of multiple <code>ChangeListener</code> for
+ * one file watched.
+ * <p>
+ * You can use this class as <i>singleton</i> calling {@link #getInstance()} or you may instance it with a constructor.
+ * Note that access to this class is <i>synchronized</i>.
+ * </p>
+ * 
+ * @author Christian Ribeaud
+ */
+public final class FileWatcherSynchronizer extends TimerTask
+{
+
+    private static final Logger operationLog =
+            LogFactory.getLogger(LogCategory.OPERATION, FileWatcherSynchronizer.class);
+
+    private static FileWatcherSynchronizer instance;
+
+    private final Map<FileWatcher, List<ChangeListener>> fileWatcherListeners;
+
+    private final Map<File, FileWatcher> fileWatchers;
+
+    public FileWatcherSynchronizer()
+    {
+        fileWatcherListeners = new HashMap<FileWatcher, List<ChangeListener>>();
+        fileWatchers = new HashMap<File, FileWatcher>();
+    }
+
+    public final static synchronized FileWatcherSynchronizer getInstance()
+    {
+        if (instance == null)
+        {
+            instance = new FileWatcherSynchronizer();
+        }
+        return instance;
+    }
+
+    public final synchronized void addChangeListener(final File file, final ChangeListener changeListener)
+    {
+        FileWatcher fileWatcher = fileWatchers.get(file);
+        if (fileWatcher == null)
+        {
+            fileWatcher = new FileWatcher(file)
+                {
+
+                    //
+                    // FileWatcher
+                    //
+
+                    @Override
+                    protected final void onChange()
+                    {
+                        fireStateChanged(this);
+                    }
+                };
+            if (operationLog.isDebugEnabled())
+            {
+                operationLog.debug(String.format("A new watcher has been created for file '%s'", file));
+            }
+            fileWatchers.put(file, fileWatcher);
+        }
+        List<ChangeListener> listeners = fileWatcherListeners.get(fileWatcher);
+        if (listeners == null)
+        {
+            listeners = new ArrayList<ChangeListener>();
+            fileWatcherListeners.put(fileWatcher, listeners);
+        }
+        listeners.add(changeListener);
+        if (operationLog.isDebugEnabled())
+        {
+            operationLog.debug(String.format(
+                    "A new listener has been registered for file '%s'. Currently %d listeners registered.", file,
+                    listeners.size()));
+        }
+    }
+
+    public final synchronized void removeChangeListener(final File file, final ChangeListener changeListener)
+    {
+        FileWatcher fileWatcher = fileWatchers.get(file);
+        if (fileWatcher == null)
+        {
+            return;
+        }
+        List<ChangeListener> listeners = fileWatcherListeners.get(fileWatcher);
+        if (listeners == null)
+        {
+            return;
+        }
+        listeners.remove(changeListener);
+        if (operationLog.isDebugEnabled())
+        {
+            operationLog.debug(String.format(
+                    "A listener for file '%s' has been removed. Currently %d listeners registered.", file, listeners
+                            .size()));
+        }
+    }
+
+    private final synchronized void fireStateChanged(FileWatcher fileWatcher)
+    {
+        final ChangeEvent event = new ChangeEvent(fileWatcher.getFileToWatch());
+        for (ChangeListener listener : fileWatcherListeners.get(fileWatcher))
+        {
+            listener.stateChanged(event);
+        }
+    }
+
+    //
+    // TimerTask
+    //
+
+    @Override
+    public final synchronized void run()
+    {
+        for (FileWatcher fileWatcher : fileWatcherListeners.keySet())
+        {
+            fileWatcher.run();
+        }
+    }
+}
\ No newline at end of file
-- 
GitLab