log4j 支持运行时修改日志的相关配置,看了一下他的source code, 用FileWatchdog这个类来做的,代码也很简单,通过循环在一定时间间隔读取配置文件,如果文件变更,调用一个doOnChange()方法。
如果自己要做一个支持运行时修改配置的系统可参考上面的做法。
下面是一段支持运行时修改配置的系统Prototype代码,和log4j的做法稍有不同,使用Observer模式,使其更加灵活。
如果某个类要在系统配置修改时得到通知,则这个类要实现Observer接口,然后调用ConfigManager.getInstance().addObserver(this);
在void update(Observable o, Object args)方法里处理相应的属性即可。
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Observable;
import java.util.Properties;
import org.apache.log4j.Logger;
import cn.heapstack.realtimeconfig.exception.ShutdownFailureException;
import cn.heapstack.realtimeconfig.exception.StartupFailureException;
import cn.heapstack.realtimeconfig.util.FileResolver;
/** *//**
* <p>
* This class holds all configuration for the system. The configuration parameters
* are read from file at configurable interval.
*
* This class implements the Singleton pattern.
*
* Because this class extends the <code>Observable</code> class, an
* <code>Observer</code> may register and get notified when the configuration
* has changed.
* </p>
*
*/
public final class DemoConfigManager extends Observable implements Runnable
{
private static DemoConfigManager myInstance = new DemoConfigManager();
private final String myName = "ConfigManager";
private static final Logger myLog = Logger.getLogger(DemoConfigManager.class);;
private File configFile = null;
private Properties myProperties = new Properties();
private boolean myIsRunning = false;
private Thread myThread = null;
/** *//**
* Default constructor.
*/
private DemoConfigManager()
{
try
{
String myConfigFile = System.getProperty("RealTimeConfigFile");
if (myConfigFile == null || "".equals(myConfigFile))
{
myConfigFile = "conf/RealTimeConfig.conf";
}
configFile = FileResolver.loadFile(myConfigFile);
InputStream is = new FileInputStream(configFile);
myProperties.load(is);
is.close();
}
catch (IOException ex)
{
System.err.println("Error reading RealTimeConfig configuration file. Will now exit RealTimeConfig: " + ex);
System.exit(-1);
}
}
/** *//**
* Return the name of this subsystem.
*/
public String getName()
{
return myName;
}
/** *//**
* Get the singleton instance of this class.
*
* @return The singleton instance.
*/
public static DemoConfigManager getInstance()
{
return myInstance;
}
/** *//**
* Loads the configuration from file.
*
* If find the configuration file changed, notify the observers
*
* @throws IOException
* If unable to open and read configuration file.
*/
private void load() throws IOException
{
InputStream is = (InputStream) new FileInputStream(configFile);
Properties aFromFileProp = new Properties();
aFromFileProp.load(is);
// Check if the properties in file has been updated compared to the stored properties.
if (!aFromFileProp.toString().equals(myProperties.toString()))
{
myProperties = aFromFileProp;
this.setChanged();
this.notifyObservers();
}
is.close();
}
/** *//**
* The run method of the ConfigManager thread. It will read the
* configuration from file every 30 seconds.
*/
public void run()
{
while (myIsRunning)
{
int interval = 30;
try
{
interval = Integer.parseInt(myProperties.getProperty("ReadConfigInterval"));
}
catch (NumberFormatException ex)
{
myLog.info(
"Error reading ReadConfigInterval config parameter. Using default value("+interval+").");
}
try
{
Thread.sleep(interval * 1000);
load();
}
catch (IOException ex)
{
myLog.info( "IO error while trying to load config file: "
+ ex.getMessage());
}
catch (InterruptedException ex)
{
}
}
}
/** *//**
* Save the configuration to file. Existing configuration data will be over
* written.
*
* @throws IOException
* If unable to open and write to file.
*/
public synchronized void save() throws IOException
{
OutputStream os = (OutputStream) new FileOutputStream(configFile);
myProperties.store(os, "RealTimeConfig");
os.close();
}
/** *//**
* Get a configuration value of a specified parameter (key).
*
* @param theKey
* The name of the parameter to fetch a value from.
*
* @return The configuration value for the specified key.
*/
public synchronized String get(String theKey)
{
return myProperties.getProperty(theKey);
}
/** *//**
* Set a configuration value of a specified parameter (key).
*
* @param theKey
* The name of the parameter to set.
*
* @param theValue
* The value of the parameter to set.
*/
public synchronized void set(String theKey, String theValue)
{
myProperties.setProperty(theKey, theValue);
}
/** *//**
* Get all parameters contained in the ConfigManager.
*
* @return A Properties object containing all configuration parameters and
* their value.
*/
public synchronized Properties getProperties()
{
return myProperties;
}
/** *//**
* Start the ConfigManager and read configuration from file. From now on the
* configuration will be read from file at a configurable interval, default
* 30 seconds.
*/
public synchronized void startup() throws StartupFailureException
{
if (myIsRunning)
{
throw new StartupFailureException("Subsystem is already running!");
}
myIsRunning = true;
myThread = new Thread(this, this.getName());
myThread.start();
}
/** *//**
* Shuts down the ConfigManager.
*
* @param theGracefulShutdownMode
* <code>true</code> if shut down should be graceful,
* <code>false</code> otherwise.
*/
public synchronized void shutdown(boolean theGracefulShutdownMode)
throws ShutdownFailureException
{
if (!this.isRunning())
{
throw new ShutdownFailureException("Subsystem is already stopped!");
}
myIsRunning = false;
try
{
myThread.interrupt();
myThread.join(1 * 60 * 1000);
}
catch (InterruptedException ex)
{
}
catch (SecurityException ex)
{
}
myThread = null;
}
/** *//**
* Checks if the ConfigManager is alive.
*
* @return <code>true</code> if alive, otherwise <code>false</code>.
*/
public boolean isRunning()
{
return (myThread != null && myThread.isAlive() && myIsRunning);
}
}
posted on 2008-09-04 14:49
jht 阅读(1306)
评论(0) 编辑 收藏 所属分类:
J2SE