// AdminServer.java
// $Id: AdminServer.java,v 1.26.2.1 2000/01/21 10:14:48 ylafon Exp $
// (c) COPYRIGHT MIT and INRIA, 1997.
// Please first read the full copyright statement in file COPYRIGHT.html

package org.w3c.jigsaw.admin;

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

import org.w3c.util.*;

import org.w3c.tools.resources.*;
import org.w3c.www.mime.*;
import org.w3c.www.http.*;
import org.w3c.jigsaw.daemon.*;
import org.w3c.jigsaw.http.*;
import org.w3c.jigsaw.auth.*;

import org.w3c.tools.resources.ProtocolException;
import org.w3c.tools.resources.ResourceException;

class ServerHandlerManagerResource extends AbstractContainer {
    ServerHandlerManager shm = null;
    AdminServer as = null;

    public ServerInterface getServer() {
	return as;
    }

    public ResourceReference lookup(String name) {
	ServerHandler handler = shm.lookupServerHandler(name);
	if (handler != null) {
	    return handler.getConfigResource();
	} 
	return null;
    }

    public Enumeration enumerateResourceIdentifiers(boolean all) {
	String name;
	Vector v = new Vector();
	Enumeration e = shm.enumerateServerHandlers();
	while(e.hasMoreElements()) {
	    name = (String) e.nextElement();
	    if( !(shm.lookupServerHandler(name) instanceof AdminServer)) {
		v.addElement(name);
	    }
	}
	v.addElement("realms");
	v.addElement("control");
	return v.elements(); 
    }

    public ResourceReference createDefaultResource(String name) {
	// This should in fact create a new server handler
	return null;
    }

    public void registerResource(String name,
				 Resource child,
				 Hashtable defs) 
    {
	throw new RuntimeException("Can't register resource there !");
    }

    public void delete(String name) {
	throw new RuntimeException("no delete supported");
    }

    ServerHandlerManagerResource(AdminServer as, ServerHandlerManager shm) {
	this.as = as;
	this.shm = shm;
    }
}

public class AdminServer extends httpd {
    protected ResourceBroker       broker           = null;
    protected ServerHandlerManager shm              = null;
    protected AdminWriter          writer           = null;
    protected AbstractContainer    root             = null;
    protected ResourceReference    rr_root          = null;
    protected ControlResource      controlConfig    = null;
    protected ResourceReference    rr_controlConfig = null;
    protected GenericAuthFilter    auth             = null;

    /**
     * The realm catalog
     */
  
    protected RealmsCatalog realms = null ;
    protected ResourceReference rr_realms = null;

    private void initializeRealmsCatalog() {
	this.realms = new RealmsCatalog(
	    new ResourceContext(getDefaultContext()),
	    "adminRealms.db");
    }
    
    public RealmsCatalog getRealmsCatalog() {
	if(realms == null) {
	    initializeRealmsCatalog();
	} 
	return realms;
    } 
  
    protected String getBanner() {
	return "JigAdmin[2.0.4]";
    } 

    public ResourceReference getConfigResource() {
	return getRootReference();
    }


    public ResourceReference getRealmCatalogResource() {
	if (rr_realms == null)
	    rr_realms = new DummyResourceReference(getRealmsCatalog());
	return rr_realms;
    }



    public ResourceReference getControlResource() {
	if (rr_controlConfig == null) {
	    controlConfig = new ControlResource(this);
	    rr_controlConfig = new DummyResourceReference(controlConfig);
	}
	return rr_controlConfig;
    }

    /**
     * Load the remote Root.
     * @param request The incomming request.
     * @exception org.w3c.tools.resources.ProtocolException if a protocol 
     * error occurs.
     */
    public Reply remoteLoadRoot(Request request) 
	throws ProtocolException
    {
	// Dump the root resource:
	ByteArrayOutputStream bout = new ByteArrayOutputStream();
	DataOutputStream      out  = new DataOutputStream(bout);
	try {
	    writer.writeResource(out, root);
	    out.close();
	} catch (IOException ex) {
	    System.out.println(ex.getMessage());
	    ex.printStackTrace();
	    throw new HTTPException("unable to dump root");
	}
	byte bits[] = bout.toByteArray();
	// Setup the reply:
	Reply reply = request.makeReply(HTTP.OK);
	reply.setStream(new ByteArrayInputStream(bits));
	reply.setContentLength(bits.length);
	reply.setContentType(ResourceBroker.conftype);
	return reply;
    }

    /**
     * List the attributes.
     * @param request The incomming request.
     * @exception org.w3c.tools.resources.ProtocolException if a protocol 
     * error occurs.
     */
    public Reply remoteListAttributes(Request request) 
	throws ProtocolException
    {
	// Get the name of the class whose attributes are being fetched:
	LookupState ls = null;
	try {
	    ls = new LookupState(request);
	} catch (org.w3c.tools.resources.ProtocolException ex) {
	    throw new HTTPException(ex);
	}
	String      cs = ls.getNextComponent();
	// Get this class attributes:
	Class c = null;
	try {
	    c = Class.forName(cs);
	} catch (Exception ex) {
	    ex.printStackTrace();
	    return null;
	}
	// Get the atributes:
	Attribute attrs[] = AttributeRegistry.getClassAttributes(c);
	byte      bits[]  = null;
	try {
	    // Dump them into a byte array:
	    ByteArrayOutputStream bout = new ByteArrayOutputStream();
	    DataOutputStream      out  = new DataOutputStream(bout);
	    writer.writeAttributes(out, attrs);
	    out.close();
	    bits = bout.toByteArray();
	} catch (Exception ex) {
	    ex.printStackTrace();
	    throw new HTTPException("couldn't dump attributes");
	}
	// Emit the appropriate HTP reply:
	Reply reply = request.makeReply(HTTP.OK);
	reply.setContentType(ResourceBroker.conftype);
	reply.setContentLength(bits.length);
	reply.setStream(new ByteArrayInputStream(bits));
	return reply;
    }

    /**
     * Perform the request
     * @param req the request.
     * @exception ProtocolException if a protocol error occurs
     * @exception ResourceException if a server error occurs
     */
    public ReplyInterface perform(RequestInterface req) 
	throws ProtocolException, ResourceException
    {
	Request request = (Request) req;
	auth.authenticate(request);
	// Maybe that's one of the methods we handle straight ?
	String mth = request.getMethod();
	if ( mth.equals("LOAD-ROOT") ) {
	    return remoteLoadRoot(request);
	} else if (mth.equals("LIST-ATTRIBUTES")) {
	    return remoteListAttributes(request);
	}
	// Ok, that's targeted toward a specific target resource:
	try {
	    return (Reply)broker.perform(request);
	} catch (org.w3c.tools.resources.ProtocolException ex) {
	    throw new HTTPException(ex);
	}
    }
    
    public FramedResource getRoot() {
	return root;
    }

    public ResourceReference getRootReference() {
	if (rr_root == null)
	    rr_root = new DummyResourceReference( root );
	return rr_root;
    }

    protected void initializeAuth() {
	Hashtable defs = null;
	defs = new Hashtable(3);
	defs.put("identifier", "auth-frame");
	root.registerFrame(auth, defs);
	auth.setValue("realm", "JigAdmin");
    }

    /**
     * Initialize the Server
     * @exception ServerHandlerInitException if it can't be initialized
     */
    public void initialize(ServerHandlerManager shm
			   , String identifier
			   , ObservableProperties props)
	throws ServerHandlerInitException
    {
	super.initialize(shm, identifier, props);
	// Create our only resource:
	this.shm = shm;
	writer   = new AdminWriter(this);
	broker   = new ResourceBroker(shm, this, writer);
	root     = new ServerHandlerManagerResource(this, shm);
	auth     = new GenericAuthFilter();
	initializeAuth();
    }

    public AdminServer() {
    }
}
