/*
* Copyright 2000-2001,2004 The Apache Software Foundation.
*
* 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 org.apache.jetspeed.services.registry;
import java.io.File;
import java.io.FileFilter;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
// Jetspeed classes
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
/**
* Monitors a Registry directory and notifies the associated Registry
* of file updates.
*
* @author <a href="mailto:raphael@apache.org">Rapha謖 Luta</a>
* @version $Id: RegistryWatcher.java,v 1.10 2004/02/23 03:31:50 jford Exp $
*/
public class RegistryWatcher extends Thread
{
/**
* Static initialization of the logger for this class
*/
private static final JetspeedLogger logger = JetspeedLogFactoryService.getLogger(RegistryWatcher.class.getName());
/** Minimum scan rate for evaluating file refresh */
public static final int SCAN_RATE = 10;
/**
The files monitored by this watcher
*/
private Hashtable files = new Hashtable();
/**
the refresh rate, in milliseconds, to use for monitoring this file
*/
private long refreshRate = 0;
/**
The object that relies on this RegsitryWatcher
*/
private FileRegistry subscriber = null;
/**
The filter to use for filtering registry files
*/
private FileFilter filter = null;
/**
* This object marks that we are done
*/
private boolean done = false;
/**
* Creates a default RegistryWatcher
*/
public RegistryWatcher()
{
setDaemon(true);
setPriority(Thread.MIN_PRIORITY);
}
/** Modifies the subscriber to this Watcher
*
* @param registry the new registry subscriber
*/
public void setSubscriber(FileRegistry registry)
{
synchronized (this)
{
if (subscriber!=null)
{
Enumeration en = files.keys();
while(en.hasMoreElements())
{
try
{
subscriber.removeFragment(((File)en.nextElement()).getCanonicalPath());
}
catch (Exception e)
{
logger.error("RegistryWatcher: Can't remove fragment", e);
}
}
}
this.subscriber = registry;
if (subscriber!=null)
{
Enumeration en = files.keys();
while(en.hasMoreElements())
{
try
{
subscriber.loadFragment(((File)en.nextElement()).getCanonicalPath());
}
catch (Exception e)
{
logger.error("RegistryWatcher: Can't load fragment", e);
}
}
}
}
}
/** @return the subscriber to this watcher */
public FileRegistry getSubscriber()
{
return this.subscriber;
}
/** Sets the refresh rate for this watcher
* @param refresh the refresh rate in seconds
*/
public void setRefreshRate(long refresh)
{
this.refreshRate = (( refresh > SCAN_RATE ) ? refresh : SCAN_RATE) * 1000;
}
/** @return the refresh rate, in seconds, of this watcher */
public long getRefreshRate()
{
return refreshRate / 1000;
}
/** Sets the file filter for selecting the registry files
* @param filter the file filter to use
*/
public void setFilter(FileFilter filter)
{
this.filter = filter;
}
/** @return the file filter used by this watcher instance */
public FileFilter getFilter()
{
return filter;
}
/** Change the base file or directory to be monitored by this watcher
*
* @param f the file or directory to monitor
*/
public void changeBase(File f)
{
synchronized (this)
{
if (this.subscriber!=null)
{
Enumeration en = files.keys();
while (en.hasMoreElements())
{
try
{
subscriber.removeFragment(((File)en.nextElement()).getCanonicalPath());
}
catch (Exception e)
{
logger.error("RegistryWatcher: Can't remove fragment", e);
}
}
}
files.clear();
findFiles(f);
}
}
/**
* Refresh the monitored file list
*
* @param f the file or directory to monitor
*/
private void findFiles(File f)
{
File[] contents = null;
if (f.exists() && f.canRead())
{
this.files.put(f,new Long(f.lastModified()));
if (f.isDirectory())
{
if (filter != null)
contents = f.listFiles(filter);
else
contents = f.listFiles();
if (contents!=null)
{
for (int i=0; i< contents.length; i++)
{
files.put(contents[i],new Long(contents[i].lastModified()));
if (subscriber!=null)
{
try
{
subscriber.loadFragment(contents[i].getCanonicalPath());
}
catch (Exception e)
{
logger.error("RegistryWatcher: Can't load fragment", e);
}
}
}
}
}
}
}
/**
* <p>Main routine for the monitor which periodically checks whether
* the filex have been modified.</p>
* The algorithm used does not guarantee a constant refresh rate
* between invocations.
*/
public void run()
{
try
{
while(!done)
{
boolean needRefresh = false;
synchronized (this)
{
Map fragments = subscriber.getFragmentMap();
if (logger.isDebugEnabled())
{
logger.debug( "RegistryWatcher: Saving dirty fragments.");
}
Iterator i = fragments.keySet().iterator();
while(i.hasNext())
{
try
{
String filename = (String)i.next();
RegistryFragment fragment = (RegistryFragment)subscriber.getFragmentMap().get(filename);
// if fragment has some uncommitted changes
if (fragment.isDirty())
{
//save it to disk
subscriber.saveFragment(filename);
if (logger.isDebugEnabled())
{
logger.debug( "RegistryWatcher: Saved " + filename);
}
//and update the stored timestamp
Enumeration en = files.keys();
while(en.hasMoreElements())
{
File f = (File)en.nextElement();
if (filename.equals(f.getCanonicalPath()))
{
files.put(f,new Long(f.lastModified()));
}
}
}
}
catch (Exception e)
{
logger.error("RegistryWatcher: exception during update",e);
}
}
if (logger.isDebugEnabled())
{
logger.debug( "RegistryWatcher: Checking for updated files.");
}
Enumeration en = files.keys();
while(en.hasMoreElements())
{
try
{
File f = (File)en.nextElement();
long modified = ((Long)files.get(f)).longValue();
if (!f.exists())
{
files.remove(f);
}
else
{
if (f.lastModified() > modified)
{
files.put(f,new Long(f.lastModified()));
if (f.isDirectory())
{
File[] contents = null;
if (filter != null)
{
contents = f.listFiles(filter);
}
else
{
contents = f.listFiles();
}
if (contents!=null)
{
for (int idx=0; idx< contents.length; idx++)
{
if (files.get(contents[idx])==null)
{
files.put(contents[idx],new Long(contents[idx].lastModified()));
if (subscriber!=null)
{
subscriber.loadFragment(contents[idx].getCanonicalPath());
}
}
}
}
}
else
{
subscriber.loadFragment(f.getCanonicalPath());
}
if (logger.isDebugEnabled())
{
logger.debug("RegistryWatcher: Refreshing because "
+ f.getCanonicalPath()
+ " was modified.("
+ f.lastModified()
+ " "
+ modified
+ ")");
}
RegistryFragment frag = (RegistryFragment)fragments.get(f.getCanonicalPath());
if (frag!=null)
{
frag.setChanged(true);
}
needRefresh = true;
}
}
}
catch (Exception e)
{
logger.error("RegistryWatcher: exception during update",e);
}
}
if (needRefresh)
{
subscriber.refresh();
needRefresh = false;
}
// make sure to reset the state of all fragments
i = fragments.keySet().iterator();
while(i.hasNext())
{
RegistryFragment frag = (RegistryFragment)fragments.get((String)i.next());
frag.setDirty(false);
frag.setChanged(false);
}
}
sleep( refreshRate );
}
}
catch (InterruptedException e)
{
logger.error("RegistryWatcher: Stopping monitor: ", e);
return;
}
}
/**
* Mark that the watching thread should be stopped
*/
public void setDone()
{
done = true;
logger.info("RegistryWatcher: Watching thread stop requested");
}
}