// Upgrade1to2.java
// $Id: Upgrade1to2.java,v 1.7 1997/02/11 13:13:26 abaird Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html

package w3c.jigsaw.upgrade;

import java.io.*;
import java.util.*;

import w3c.jigsaw.upgrade.from1to2.*;
import w3c.jigsaw.http.httpd;

public class Upgrade1to2 implements Upgrader {
    public final static int DO_STORES  = (1<<0);
    public final static int DO_PROPS   = (1<<1);
    public final static int DO_AUTH    = (1<<2);
    public final static int DO_INDEXER = (1<<3);

    static Hashtable propertyDescriptions = new Hashtable(23);
    static {
	Hashtable d = propertyDescriptions;
	d.put("w3c.jigsaw.http.ClientPool.minIdle", Boolean.FALSE);
	d.put("w3c.jigsaw.http.ClientPool.minFree", Boolean.FALSE);
	d.put("w3c.jigsaw.http.ClientPool.maxFree", Boolean.FALSE);
	d.put("w3c.jigsaw.http.ClientPool.maxIdle", Boolean.FALSE);
    }

    Properties p = null;
    File confdir = null;
    int  flags   = 0;
    PrintStream prttrace = null;
    File        tracefile = null;

    /**
     * Emit an upgrade trace:
     * @param msg The message to emit.
     */

    protected String indent = "";
    public void trace(String msg) {
	prttrace.println(indent+msg);
    }

    public void trace(String indent, String msg) {
	this.indent = "";
	trace(msg);
	this.indent = indent;
    }
    
    public void warning(String msg) {
	prttrace.println("* "+msg);
    }

    public void error(String msg) {
	prttrace.println("*** Error: "+msg);
    }

    /**
     * Emit a message, upgrade has performed successfully.
     */
    
    public void upgradeDone() {
	System.err.println("+ Upgrade 1.0alpha3 to 1.0alpha4 successfull.");
	prttrace.println("If any problem, check the file "
		         + tracefile
			 + " for lines starting with \"***\" (errors) "
			 + " or lines starting with \"*\" (warnings).");
	trace("upgrade finished.");
    }

    protected void createServerHandlerProperties() {
	File file = new File(confdir, "server.props");
	// Check if that file already exists:
	if ( file.exists() ) {
	    warning("Won't upgrade \"server.props\" in "+confdir
		  + ": file already exists.");
	    return;
	}
	// Create it otherwise:
	try {
	    PrintStream prt = new PrintStream(new FileOutputStream(file));
	    prt.println("w3c.jigsaw.daemon.handlers="
			+ "http-server");
	    prt.println("http-server.w3c.jigsaw.daemon.class="
			+ "w3c.jigsaw.http.httpd");
	    prt.close();
	} catch (Exception ex) {
	    error("unable to create default ServerHandlerManager properties "
		  + " in file ["+file.getAbsolutePath() + "]: "
		  + "" + ex.getMessage() +".");
	}
	trace("\"server.props\" properties created.");
    }

    /**
     * Compute this property prefix.
     */
    private String getPropertyPrefix(String key) {
	int i = key.indexOf('.');
	return ( i >= 0 ) ? key.substring(0, i) : null;
    }

    private String getPropertyPostfix(String key) {
	int i = key.indexOf('.');
	return (((i < 0) || (i >= key.length()))
		? null 
		: key.substring(i+1));
    }

    protected void removePropertiesPrefix() {
	String      prefix = null;
	Enumeration enum   = p.keys();
	while ( enum.hasMoreElements() ) {
	    String prekey = (String) enum.nextElement();
	    String pre    = getPropertyPrefix(prekey);
	    String post   = getPropertyPostfix(prekey);
	    // Compute property prefix first time:
	    if ( prefix == null ) {
		if ((pre != null) && pre.equals("http-server") ) {
		    prefix = pre;
		    trace("using \""+prefix+"\" as the property prefix.");
		} else {
		    warning("skipping prefix \""+prefix+"\".");
		}
	    }
	    // What should we do with this property now ?
	    if ((pre != null) && pre.equals(prefix) && (post != null)) {
		// What should we do with this property:
		Object what = propertyDescriptions.get(post);
		if ( what instanceof Boolean ) {
		    // Skip it, it's now deprecated
		    trace(prekey+": removed (deprecated).");
		    p.remove(prekey);
		    continue;
		} else if ( what instanceof String ) {
		    // This property has been renamed:
		    String newkey = (String) what;
		    trace(prekey+": renamed to "+newkey);
		    p.put(newkey, p.get(post));
		    p.remove(prekey);
		} else /* what is null */ {
		    // Normal upgrade this property name:
		    if ( p.get(post) != null ) {
			warning(prekey
				+ ": not renamed ("
				+ "key \"" + post + "\" already exists.)");
			continue;
		    } else if (post.equalsIgnoreCase(httpd.SERVER_SOFTWARE_P)){
			warning(prekey
				+ ": set to \"1.0alpha5\".");
			p.put(post, "Jigsaw/1.0alpha5");
		    } else {
			trace(prekey +": renamed to \"" + post +"\".");
			p.put(post, p.get(prekey));
			p.remove(prekey);
		    }
		}
	    } else {
		trace(prekey+": not renamed.");
	    }
	}
    }

    protected void saveProperties() {
	File newfile = new File(confdir, "http-server.props");
	try {
	    OutputStream out = new FileOutputStream(newfile);
	    p.save(out, "# Saved by \""
                         + getClass().getName()
			 + "\" on "+new Date());
	} catch (Exception ex) {
	    error("unable to save upgraded properties to ["
		  + newfile.getAbsolutePath()
		  + "]: "+ex.getMessage());
	}
	trace("upgraded properties save to \""
              + newfile.getAbsolutePath()
	      + "\".");
    }

    /**
     * Upgrade the properties.
     * The difference between version 1 and version 2, with regard to 
     * properties is:
     * <ul>
     * <li>Properties prefix are now deprecated.
     * <li>Instead, we use several different files per server handler.
     * <li>We also create the default <em>server.props</em> file.
     * </ul>
     */

    protected void upgradeProperties() {
	trace("\t", "upgrading properties...");
	// Bump the version counter:
	removePropertiesPrefix();
	p.put("w3c.jigsaw.version.counter", "2");
	saveProperties();
	createServerHandlerProperties();
	trace ("", "properties upgraded successfully.");
    }

    /**
     * Upgrade resource stores.
     */

    protected void upgradeSimpleResourceStore(File repository) {
	int count = 0;
	trace("\t\t", "\tnow upgrading: "+repository);
	// This is kind of hairy, but it really doesn't matter, beleive me
	try {
	    SimpleResourceStore store = new SimpleResourceStore();
	    store.initialize(repository);
	    Enumeration enum = store.enumerateResourceIdentifiers();
	    while ( enum.hasMoreElements() ) {
		count++;
		String id = (String) enum.nextElement();
		try {
		    store.upgrade(id);
		} catch (Exception ex) {
		    error("Unable to upgrade resource "
			  + id + " of store " + repository
			  + " use the -force flag to drop that resource.");
		}
	    }
	    store.save();
	} catch (Exception ex) {
	    ex.printStackTrace();
	    error("unable to upgrade resource store \""
		  + repository
		  + "\".");
	}
	trace(repository+": "+count+" resources upgraded.");
    }

    protected void upgradeStores() {
	File storedir   = new File(confdir, "stores");
	trace ("\t", "upgrading resource stores in "+storedir+"...");
	// Remove resource manager state, to have it salvaged !
	File fstate = new File(storedir, "state");
	if ( fstate.exists() ) 
	    fstate.delete();
	// Upgrade all available stores:
	String stores[] = storedir.list(new StoreDirectoryFilter());
	for (int i = 0 ; i < stores.length; i++)
	    upgradeSimpleResourceStore(new File(storedir, stores[i]));
	trace("", "all resource stores upgraded.");
    }

    public void upgradeAuthInfos() {
	File authdir = new File(confdir, "auth");
	trace ("\t", "upgrading authentication stores in "+authdir+"...");
	String stores[] = authdir.list(new StoreDirectoryFilter());
	for (int i = 0 ; i < stores.length; i++) 
	    upgradeSimpleResourceStore(new File(authdir, stores[i]));
	trace("Authentication database upgraded.");
    }

    public void upgradeIndexer() {
	trace ("\t", "upgrading indexer stores in "+confdir+"...");
	upgradeSimpleResourceStore(new File(confdir, "exts.db"));
	upgradeSimpleResourceStore(new File(confdir, "dirs.db"));
	trace("Indexer stores upgraded.");
    }

    public void upgrade(String args[])
	throws UpgradeException
    {
	parse(args);
	this.tracefile = new File(confdir, "upgrade1to2.txt");
	System.err.println("+ Upgrading config from 1.0alpha3 to 1.0alpha4");
	System.err.println("+ Will emit traces in file " + tracefile);
	try {
	    this.prttrace  = new PrintStream(new FileOutputStream(tracefile));
	} catch (Exception ex) {
	    System.err.println("Unable to create trace file, using stderr.");
	    this.prttrace = System.err;
	}
	// Upgrade the property file:
	if ((flags & DO_PROPS) == DO_PROPS)
	    upgradeProperties();
	// Upgrade the stores:
	if ((flags & DO_STORES) == DO_STORES)
	    upgradeStores();
	// Upgrade authentication catalogs and realms
	if ((flags & DO_AUTH) == DO_AUTH)
	    upgradeAuthInfos();
	// Upgrade Extensions and Directories database:
	if ((flags & DO_INDEXER) == DO_INDEXER)
	    upgradeIndexer();
	// That's it for differences between version 1 and 2
	upgradeDone();
    }

    protected void parse(String args[])
	throws UpgradeException
    {
	String root = null;
	String conf = null; 
	for (int i = 0 ; i < args.length; i++) {
	    if ( args[i].equals("-config") && (i+1 < args.length)) {
		conf = args[++i];
	    } else if ( args[i].equals("-store") ) {
		this.flags += DO_STORES;
	    } else if ( args[i].equals("-auth") ) {
		this.flags += DO_AUTH;
	    } else if ( args[i].equals("-props") ) {
		this.flags += DO_PROPS;
	    } else if ( args[i].equals("-indexer") ) {
		this.flags += DO_INDEXER;
	    } else if ( args[i].equals("-root") && (i+1 < args.length)) {
		root = args[++i];
	    } else {
		continue;
	    }
	}
	// Check values, or use defaults:
	File rootdir = ((root == null)
			? new File(System.getProperty("user.dir", null))
			: new File(root));
	this.confdir = ((conf == null)
			? new File(rootdir, "config")
			: new File(rootdir, conf));
	if ((confdir == null) || ! confdir.exists() ) {
	    String err = ("Unable to locate Jigsaw config directory "
			  + ((confdir != null) ? confdir.toString() : "\"\"")
			  + ". Restart Jigsaw with the -config option.");
	    throw new UpgradeException(err);
	}
	// Set flags to all if not specified:
	if ( flags == 0 )
	    flags = DO_PROPS+DO_AUTH+DO_STORES+DO_INDEXER;
	// Load properties:
	this.p = new Properties();
	try {
	    p.load(new FileInputStream(new File(confdir, "httpd.props")));
	} catch (Exception ex) {
	    String err = ("Unable to load default properties from "
			  + new File(confdir, "httpd.props")
			  + ". Restart Jigsaw with the -p option.");
	    throw new UpgradeException(err);
	}
    }

    public static void usage() {
	PrintStream p = System.out;
	p.println("upgrade -config <config directory>");
	p.println("\t[-store]: upgrade all resource stores only.");
	p.println("\t[-auth]: upgrade auth infos only.");
	p.println("\t[-props]: upgrade properties only.");
	p.println("\t[-indexer]: upgrade indexer only.");
    }

    public static void main(String args[]) {
	Upgrade1to2 upgrader = new Upgrade1to2();
	try {
	    upgrader.upgrade(args);
	} catch (UpgradeException ex) {
	    System.err.println("*** Unable to upgrade configuration.");
	    ex.printStackTrace();
	}
    }

    public Upgrade1to2() {
	super();
    }

}
