[Prev][Next][Index][Thread]
A rough around the edge auto-saver
If everything is ok, you should be able to do the following:
a) Install the attached file in
Jigsaw/src/classes/w3c/jigsaw/contrib/CheckpointResource.java
b) Compile that class
c) Install it into your URL space (typically in /Admin)
This resource will checkpoint your configuration every N seconds. Note
that this is roughly tested code, and not all features are implemented
(prop and log flush).
Problems:
- Unless you access it, it will not start (so you really have to GET
/Admin/checkpoint for it to start)
- If the resource gets unloaded for some reason, it will not start up
again.
These are two serious problems, my intent is:
a) For the first problem, maintain a list of resources that are to be
loaded at startup time, ie, have a httpd property giving the name
of a file consisting of a list of resource URL. After init, Jigsaw
will look them up (which as a side effect will load them into
memory) - This is I guess, what Alex suggested this morning.
b) The second problem is more complicated, it requires some API
changes to be able, for a resource to reject a notifyUnload. Unless
I find something better, I will probably make notifyUnload return a
boolean, if false, the resource will *not* be unloaded. Alternative
is to add a new API in w3c.jigsaw.resources.Resource, eg:
public boolean isUnloadable()
which would return true by default, before deciding to unload a
resource, a resource store would invoke that method and behalf
according to the result. Alternative allows for backward
compatibility, but I guess no one have played with notifyUnload
yet (?) (drawback of alternative is that notifyUnload is no longer
atomic, which might be a problem...)
Criticisms, advices and opinions on these problems most welcome.
Anselm.
// CheckpointResource.java
// $Id$
// (c) COPYRIGHT MIT and INRIA, 1996.
// please first read the full copyright statement in file COPYRIGHT.HTML
package w3c.jigsaw.contrib;
import w3c.www.http.*;
import w3c.jigsaw.http.*;
import w3c.jigsaw.resources.*;
import w3c.jigsaw.html.*;
import java.util.*;
/**
* A resource that will checkpoint the configuration at regular intervals.
* This resource will make sure that current configuration is backed up to
* disk at regular (configurable) intervals.
* <p>The webmaster can customize what part of the configuration is to be
* backed up through boolean attributes.
*/
public class CheckpointResource extends FilteredResource implements Runnable {
/**
* Attribute index - Backup interval, in seconds.
*/
protected static int ATTR_INTERVAL = -1;
/**
* Attribute index - The priority of the flusher thread.
*/
protected static int ATTR_PRIORITY = -1;
/**
* Attribute index - Should we flush the logs too ?
*/
protected static int ATTR_FLUSHLOG = -1;
/**
* Attrbute index - Should we save the properties too ?
*/
protected static int ATTR_FLUSHPROPS = -1;
/**
* Attribute index - Should we save the configuration ?
*/
protected static int ATTR_FLUSHCONFIG = -1;
static {
Class c = null;
Attribute a = null;
try {
c = Class.forName("w3c.jigsaw.contrib.CheckpointResource");
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
}
// Register the interval attribute:
a = new IntegerAttribute("interval"
, new Integer(60)
, Attribute.EDITABLE);
ATTR_INTERVAL = AttributeRegistry.registerAttribute(c, a);
// Register the flusher thread priority
a = new IntegerAttribute("thread-priority"
, new Integer(2)
, Attribute.EDITABLE);
ATTR_PRIORITY = AttributeRegistry.registerAttribute(c, a);
// Register the flushlog boolean property:
a = new BooleanAttribute("flush-logs"
, Boolean.FALSE
, Attribute.EDITABLE);
ATTR_FLUSHLOG = AttributeRegistry.registerAttribute(c, a);
// Register the flush properties property:
a = new BooleanAttribute("flush-properties"
, Boolean.FALSE
, Attribute.EDITABLE);
ATTR_FLUSHPROPS = AttributeRegistry.registerAttribute(c, a);
// Register the flush configuration property:
a = new BooleanAttribute("flush-configuration"
, Boolean.TRUE
, Attribute.EDITABLE);
ATTR_FLUSHCONFIG = AttributeRegistry.registerAttribute(c, a);
}
/**
* Our thread, if one is currently attached.
*/
protected Thread thread = null;
/**
* Last date at which we checkpointed the configuration
*/
protected Date checkpoint = null;
/**
* Is our attached thread still alive ?
*/
protected boolean alive = false;
/**
* Start the thread for this object, only if needed.
*/
protected synchronized void activate() {
if (getFlushLog() || getFlushProperties() || getFlushConfiguration()) {
if (getInterval() > 0) {
alive = true;
if (thread == null) {
thread = new Thread(this);
thread.setName("checkpointer");
thread.setPriority(getPriority());
thread.start();
return;
} else {
return;
}
}
}
// The thread should not be running any more, stop and kill it:
if ( thread != null ) {
alive = false;
thread.stop();
thread = null;
}
return;
}
/**
* Force our attached thread to stop.
*/
protected synchronized void stop() {
alive = false;
thread = null;
notify();
}
/**
* Get the sync interval.
* @return An integer number of seconds, or <strong>-1</strong> if
* undefined.
*/
public int getInterval() {
return getInt(ATTR_INTERVAL, -1);
}
/**
* Get the priority for our attached thread.
* @return An integer priority for the thread, which defaults to
* <strong>2</strong> if undefined.
*/
public int getPriority() {
return getInt(ATTR_PRIORITY, 2);
}
/**
* Get the flush log flag.
* @return A boolean, <strong>true</strong> if the log is to be flushed at
* each refresh interval, <strong>false</strong> otherwise.
*/
public boolean getFlushLog() {
return getBoolean(ATTR_FLUSHLOG, false);
}
/**
* Get the flush properties flag.
* @return A boolean, <strong>true</strong> if the properties are to be
* flushed, <strong>false</strong> otherwise.
*/
public boolean getFlushProperties() {
return getBoolean(ATTR_FLUSHPROPS, false);
}
/**
* Get the flush configuration flag.
* @return A boolean, <strong>true</strong> oif the configuration is to be
* flushed at each interval, <strong>false</strong> otherwise.
*/
public boolean getFlushConfiguration() {
return getBoolean(ATTR_FLUSHCONFIG, true);
}
/**
* This resource is being unloaded.
* Unloading that object will also stop the thread. However, there is
* a bug here, since if the resource gets unloaded for some reason, it
* will not be able to wakeup itself at next checkpoint time.
*/
public void notifyUnload() {
stop();
super.notifyUnload();
}
/**
* We are attached a thread, now it's time to performt the job.
*/
public void run() {
httpd server = getServer();
int interval = -1;
try {
while ( alive ) {
// Are we still alive ?
interval = getInterval();
alive = ((getFlushLog()
|| getFlushProperties()
|| getFlushConfiguration())
&& (interval > 0 ));
if ( ! alive )
break;
// Wait for something to do:
synchronized(this) {
try {
wait(interval*1000);
} catch (InterruptedException ex) {
}
}
// Do what is to be done:
if (alive && getFlushConfiguration() )
server.checkpoint();
checkpoint = new Date();
}
} catch (Exception ex) {
String msg = (getClass().getName() + "@" + getURL()
+": exception while running \""
+ ex.getMessage() + "\". Stopped.");
server.errlog(msg);
} finally {
thread = null;
}
}
/**
* Get the content of that resources.
* Will display some usefull commands to start/stop the attached thread
* @param request The request to handle.
* @exception HTTPException If request processing failed.
*/
public Reply get(Request request)
throws HTTPException
{
String query = request.getQueryString();
if ( query != null ) {
if ( query.equals("start") ) {
// Start the thread if needed
activate();
} else if (query.equals("stop") ) {
// Stop the thread
stop();
}
}
// Emit output:
HtmlGenerator g = new HtmlGenerator("CheckpointResource");
g.append("<h1>CheckpointResource status</h1>");
g.append("<p>Checkpoint is currently "
, ((thread == null) ? " stopped " : "running")
, ".");
g.append("<hr>You can:<p><dl>");
g.append("<dt><a href=\""
, getURL()
, "?start\">start</a><dd>Start the checkpointer.");
g.append("<dt><a href=\""
, getURL()
, "?stop\">stop</a><dd>Stop the checkpointer.");
g.append("</dl><hr>Last checkpoint at <strong>"
, ((checkpoint == null)
? "no checkpoint run yet"
: checkpoint.toString())
, "</strong>.");
Reply reply = createDefaultReply(request, HTTP.OK);
reply.setStream(g);
return reply;
}
/**
* Activate the checkpointer at initialization time.
* @poaram values Default attribute values.
*/
public void initialize(Object values[]) {
super.initialize(values);
activate();
}
}