// CheckpointResource.java
// $Id: CheckpointResource.java,v 1.5 1998/03/12 12:18:59 bmahe Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// please first read the full copyright statement in file COPYRIGHT.HTML

package org.w3c.jigsaw.resources;

import org.w3c.tools.resources.*;
import org.w3c.www.http.*;
import org.w3c.jigsaw.http.*;

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 FramedResource implements Runnable {

  private static final boolean debug = true;

  /**
   * 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;
 /**
   * Attribute index - should we display a trace when we perform checkpoint
   */
  protected static int ATTR_TRACE_CHECK = -1;

  static {
    Class     c = null;
    Attribute a = null;

    try {
      c = Class.forName("org.w3c.jigsaw.resources.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);
    // Register the trace property
    a = new BooleanAttribute("trace-checkpoint"
			     , Boolean.FALSE
			     , Attribute.EDITABLE);
    ATTR_TRACE_CHECK = 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.
   */

  public 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);
  }

  public boolean getTraceFlag() {
    return getBoolean(ATTR_TRACE_CHECK, 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 is the only resource that will refuse to be unloaded !
   * @return Always <strong>false</strong>.
   */

  public boolean acceptUnload() {
    return false;
  }

  /**
   * 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   = (httpd) 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:
	checkpoint = new Date();
	if ( getTraceFlag() )
	  System.out.println("*** Checkpoint ("+
			     server.getIdentifier()+
			     ") at "+
			     checkpoint);
	if (alive && getFlushConfiguration() ) 
	  server.checkpoint();
	if (alive && getFlushLog()) {
	  Logger logger = ((httpd)getServer()).getLogger();
	  if ( logger != null)
	    logger.sync();
	}
      }
    } catch (Exception ex) {
      String msg = ("exception while running \""+
		    ex.getMessage() + "\". Stopped.");
      server.errlog(this, msg);
    } finally {
      thread = null;
    }
  }

  /**
   * Activate the checkpointer at initialization time.
   * @poaram values Default attribute values.
   */
  
  public void initialize(Object values[]) {
    super.initialize(values);
    try {
      registerFrameIfNone("org.w3c.jigsaw.resources.CheckpointFrame",
			  "checkpoint-frame");
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
  
  
}
