/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/tools/config/WebXml.java,v 1.6 2004/09/15 14:58:25 pnever Exp $
 * $Revision: 1.6 $
 * $Date: 2004/09/15 14:58:25 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.slide.store.tamino.tools.config;

import com.softwareag.common.instrumentation.logging.Level;
import com.softwareag.common.instrumentation.logging.Logger;
import com.softwareag.common.instrumentation.logging.LoggerFactory;
import com.softwareag.common.instrumentation.logging.LoggerUtil;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Iterator;
import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.store.tamino.tools.Env;
import org.apache.slide.util.ClassName;
import org.apache.slide.util.JDom;
import org.apache.slide.util.MimeMapping;
import org.apache.slide.util.PublicIdResolver;
import org.apache.slide.util.XException;
import org.jaxen.JaxenException;
import org.jaxen.XPath;
import org.jaxen.jdom.JDOMXPath;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;
import org.jdom.output.Format;

/**
 ** Utility to handle security specific configuration information contained in the Web.XML
 ** file.
 **
 ** @author    eckehard.hermann@softwareag.com
 ** @version   $Revision: 1.6 $
 **/
public class WebXml implements XGlobals {
    private static final String LOGNAME = LoggerUtil.getThisClassName();
    private static final String CLASSNAME = new ClassName(LOGNAME).getPlainName();
    private static Logger logger = LoggerFactory.getLogger(LOGNAME);
    
    /** The name of the Domain file */
    private static final String WEB_XML             = "web.xml";
    private static final String WEB_INF             = "WEB-INF";
    private static final String SECURITY_CONSTRAINT = "security-constraint";
    private static final String LOGIN_CONFIG        = "login-config";
    private static final String SECURITY_ROLE       = "security-role";
    private static final String SEC_TEMP_FILE       = "SecurityConstraints.xml";
    private static final String ETC                 = "etc";
    
    /** The Web.XML document */
    private final Document doc;
    
    /** The Web.XML file */
    private final File file;
    
    /** The SecurityTemplate.XML document */
    private final Document templateDoc;
    
    /** The SecurityTemplate.XML file */
    private final File templateFile;
    
    /**
     ** Default constructor.
     **
     ** @exception  XException when loading the XML file fails
     **/
    public WebXml() throws XException {
        String webXmlPath = Env.get().home+File.separator+WEB_INF+File.separator+WEB_XML;
        String secTempPath = Env.get().home+File.separator+ETC+File.separator+SEC_TEMP_FILE;
        SAXBuilder webBuilder = new SAXBuilder();
        
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "<init>", new Object[] {webXmlPath}  );
        
        this.file = new File( webXmlPath );
        if( !file.exists() ) {
            throw new XException( "File "+webXmlPath+" not found" );
        }
        
        this.templateFile = new File( secTempPath );
        this.templateDoc = JDom.forFile(templateFile);
        
        // parse the domain file
        try {
            PublicIdResolver er = new PublicIdResolver();
            er.map( PublicIdResolver.WEB_APP_DTD_PUBLID_ID, PublicIdResolver.WEB_APP_DTD_JAVAPATH );
            webBuilder.setEntityResolver( er );
            webBuilder.setValidation(false);
            this.doc = webBuilder.build( file );
        }
        catch( JDOMException x ) {
            throw new XException( "Parsing of "+webXmlPath+" failed", x );
        }
        catch( IOException x ) {
            throw new XException( "Parsing of "+webXmlPath+" failed", x );
        }
        
        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "<init>" );
    }
    
    public MimeMapping readMimeMapping() throws XException {
        MimeMapping mm;
        
        mm = new MimeMapping();
        mm.addAll(doc);
        return mm;
    }
    
    /**
     * TODO: i'd love to use the same code as the slide servlet, but I'm not necessarily
     * running in a servlet.
     */
    public String getNamespace() throws XException, JaxenException {
        XPath path;
        Iterator iter;
        Element element;
        
        path = new JDOMXPath ("/web-app/servlet/init-param");
        iter = path.selectNodes(doc).iterator();
        while (iter.hasNext()) {
            element = (Element) iter.next();
            if ("namespace".equals(JDom.getElement(element, "param-name").getTextTrim())) {
                return JDom.getElement(element, "param-value").getTextTrim();
            }
        }
        throw new XException("namespace parameter not found");
    }
    
    /**
     ** adds the SECURITY_CONSTRAIN and LOGIN_CONFIG settings
     **
     ** @pre        true
     ** @post       true
     **
     ** @exception  XException
     **/
    public void addSecurity() throws XException {
        doRemoveSecurity();
        doAddSecurity();
        save();
    }
    
    /**
     ** removes the SECURITY_CONSTRAIN and LOGIN_CONFIG settings
     **
     ** @pre        true
     ** @post       true
     **
     ** @exception  XException
     **/
    public void removeSecurity() throws XException {
        doRemoveSecurity();
        save();
    }
    
    
    //--
    
    /**
     ** Commit changes to the Web.xml file.
     **
     ** @pre        true
     ** @post       true
     **
     ** @exception  XException
     **/
    private synchronized void save() throws XException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "commit" );
        
        try {
            OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(file),"ISO-8859-1");
            XMLOutputter webXmlOut = JDom.outputter();
            webXmlOut.getFormat().setTextMode(Format.TextMode.NORMALIZE);
            webXmlOut.getFormat().setEncoding("ISO-8859-1");
            webXmlOut.output(doc, out);
            out.flush();
            out.close();
            if( logger.isLoggable(Level.INFO) )
                logger.info( CLASSNAME, "commit", "Saved file '"+file+"' successfully" );
        }
        catch( IOException x ) {
            throw new XException( "Saving file '"+file+"' failed", x );
        }
        
        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "commit" );
    }
    
    /**
     ** Get the root element.
     **
     ** @pre        true
     ** @post       true
     **
     ** @return     the root element
     **
     ** @exception  XException if root element missing
     **/
    private Element getRootElement() throws XException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "getRootElement"  );
        
        // get the root
        Element result = doc.getRootElement();
        if( result == null )
            throw new XException( "File "+file+": missing root element (web-app)" );
        
        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "getRootElement", result );
        return result;
    }
    
    /**
     ** removes the SECURITY_CONSTRAIN and LOGIN_CONFIG element
     **
     ** @pre        true
     ** @post       true
     **
     ** @exception  XException
     **/
    private void doRemoveSecurity() throws XException {
        doRemoveSecurityConstrain();
        doRemoveLoginConfig();
        doRemoveSecurityRole();
    }
    
    private void doAddSecurity() throws XException {
        // get the root
        Element secTempRoot = templateDoc.getRootElement();
        if( secTempRoot == null )
            throw new XException( "File "+templateFile.getPath()+": missing root element (web-app)" );
        Element webRoot = getRootElement();
        Element secCon = secTempRoot.getChild(SECURITY_CONSTRAINT);
        Element secLogin = secTempRoot.getChild(LOGIN_CONFIG);
        Element secRole = secTempRoot.getChild(SECURITY_ROLE);
        // add SECURITY_CONSTRAINT and LOGIN_CONFIG
        webRoot.addContent((Element)secCon.clone());
        webRoot.addContent((Element)secLogin.clone());
        if (secRole != null) {
            webRoot.addContent((Element)secRole.clone());
        }
    }
    
    private void doRemoveSecurityConstrain() throws XException {
        Element root = getRootElement();
        root.removeChildren(SECURITY_CONSTRAINT);
    }
    
    private void doRemoveLoginConfig() throws XException {
        Element root = getRootElement();
        root.removeChild(LOGIN_CONFIG);
    }
    
    private void doRemoveSecurityRole() throws XException {
        Element root = getRootElement();
        root.removeChild(SECURITY_ROLE);
    }
}


