/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/store/XParentStore.java,v 1.3 2004/07/30 06:52:03 ozeigermann Exp $
 * $Revision: 1.3 $
 * $Date: 2004/07/30 06:52:03 $
 *
 * ====================================================================
 *
 * 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.store;

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.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import org.apache.slide.common.Namespace;
import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.Scope;
import org.apache.slide.common.Service;
import org.apache.slide.common.ServiceInitializationFailedException;
import org.apache.slide.common.ServiceParameterErrorException;
import org.apache.slide.common.ServiceParameterMissingException;
import org.apache.slide.content.NodeProperty.NamespaceCache;
import org.apache.slide.search.basic.BasicSearchLanguage;
import org.apache.slide.store.StandardStore;
import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.store.tamino.datastore.XDbHandler;
import org.apache.slide.store.tamino.store.XAdministrationStore;
import org.apache.slide.store.tamino.store.XUnavailableStore;
import org.apache.slide.store.tamino.store.monitoring.IMonitor;
import org.apache.slide.store.tamino.store.monitoring.IMonitorable;
import org.apache.slide.store.tamino.store.monitoring.Monitor;
import org.apache.slide.store.tamino.store.monitoring.MonitoredCounter;
import org.apache.slide.store.tamino.store.monitoring.MonitoredTimer;
import org.apache.slide.store.tamino.tools.repairer.XRepairerStore;
import org.apache.slide.store.tamino.tools.stores.XStore;
import org.apache.slide.util.ClassName;
import org.apache.slide.util.XException;
import org.apache.slide.util.XUri;


/**
 ** XDAV-specific extension of org.apache.slide.store.StandardStore
 ** to allow monitoring.
 **
 ** @author    peter.nevermann@softwareag.com
 **
 ** @version   $Revision: 1.3 $
 **/
public class XParentStore extends StandardStore
    implements ISlideAccessor, IMonitorable, XGlobals {

    private static final String LOGNAME = LoggerUtil.getThisClassName();
    private static final String CLASSNAME = new ClassName(LOGNAME).getPlainName();
    private static Logger logger = LoggerFactory.getLogger(LOGNAME);

    // constants
    private final static String TAMINO_URL = "Tamino URL";
    private final static String REPAIR_MODE = "Repair mode";
    private final static String CREATE_CONTENT_COUNTER = "Create content [#]";
    private final static String CREATE_CONTENT_TIMER = "Create content [ms]";
    private final static String RETRIEVE_CONTENT_COUNTER = "Retrieve content [#]";
    private final static String RETRIEVE_CONTENT_TIMER = "Retrieve content [ms]";
    private final static String STORE_CONTENT_COUNTER = "Store content [#]";
    private final static String STORE_CONTENT_TIMER = "Store content [ms]";
    private final static String REMOVE_CONTENT_COUNTER = "Remove content [#]";
    private final static String REMOVE_CONTENT_TIMER = "Remove content [ms]";
    private final static String CREATE_OBJ_COUNTER = "Create object [#]";
    private final static String CREATE_OBJ_TIMER = "Create object [ms]";
    private final static String RETRIEVE_OBJ_COUNTER = "Retrieve object [#]";
    private final static String RETRIEVE_OBJ_TIMER = "Retrieve object [ms]";
    private final static String STORE_OBJ_COUNTER = "Store object [#]";
    private final static String STORE_OBJ_TIMER = "Store object [ms]";
    private final static String REMOVE_OBJ_COUNTER = "Remove object [#]";
    private final static String REMOVE_OBJ_TIMER = "Remove object [ms]";
    private final static String PREPARE_COUNTER = "Prepare [#]";
    private final static String PREPARE_TIMER = "Prepare [ms]";
    private final static String COMMIT_COUNTER = "Commit [#]";
    private final static String COMMIT_TIMER = "Commit [ms]";
    private final static String ROLLBACK_COUNTER = "Rollback [#]";
    private final static String ROLLBACK_TIMER = "Rollback [ms]";


    /** True, if this store is initialized. */
    protected boolean initialized = false;

    /** The parameters (from Domain.xml). */
    //private Hashtable parameters = null;

    /** The namespace access token */
    protected NamespaceAccessToken nsaToken = null;

    /** The associated DB handler */
    final XDbHandler dbHandler;

    // monitoring...
    /** the name */
    protected String monName = null;
    /** the parent */
    protected IMonitorable monParent = null;
    /** the children */
    protected List monChildren = new ArrayList();
    /** the Monitor object */
    protected Monitor monitor = null;


    /** If true, this store is READ_ONLY */
    private boolean repairMode;


    // monitoring counters and timers...
    /** retrieveContentCounter **/
    protected MonitoredCounter retrieveContentCounter = null;
    /** retrieveContentTimer **/
    protected MonitoredTimer retrieveContentTimer = null;
    /** storeContentCounter **/
    protected MonitoredCounter storeContentCounter = null;
    /** storeContentTimer **/
    protected MonitoredTimer storeContentTimer = null;
    /** createContentCounter **/
    protected MonitoredCounter createContentCounter = null;
    /** createContentTimer **/
    protected MonitoredTimer createContentTimer = null;
    /** removeContentCounter **/
    protected MonitoredCounter removeContentCounter = null;
    /** removeContentTimer **/
    protected MonitoredTimer removeContentTimer = null;
    /** createObjectCounter **/
    protected MonitoredCounter createObjectCounter = null;
    /** createObjectTimer **/
    protected MonitoredTimer createObjectTimer = null;
    /** retrieveObjectCounter **/
    protected MonitoredCounter retrieveObjectCounter = null;
    /** retrieveObjectTimer **/
    protected MonitoredTimer retrieveObjectTimer = null;
    /** storeObjectCounter **/
    protected MonitoredCounter storeObjectCounter = null;
    /** storeObjectTimer **/
    protected MonitoredTimer storeObjectTimer = null;
    /** removeObjectCounter **/
    protected MonitoredCounter removeObjectCounter = null;
    /** removeObjectTimer **/
    protected MonitoredTimer removeObjectTimer = null;
    /** prepareCounter **/
    protected MonitoredCounter prepareCounter = null;
    /** prepareTimer **/
    protected MonitoredTimer prepareTimer = null;
    /** commitCounter **/
    protected MonitoredCounter commitCounter = null;
    /** commitTimer **/
    protected MonitoredTimer commitTimer = null;
    /** rollbackCounter **/
    protected MonitoredCounter rollbackCounter = null;
    /** rollbackTimer **/
    protected MonitoredTimer rollbackTimer = null;


    /**
     ** Default constructor.
     **/
    public XParentStore() {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "<init>" );

        // get the DB handler
        dbHandler = new XDbHandler( this );
        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "<init>" );
    }

    /**
     * Return true, if this store is initialized.
     * @return true, if this store is initialized
     */
    public boolean isInitialized() {
        return initialized;
    }

    /**
     ** Get the associated namespace.
     **
     ** @return     return the namespace
     **/
    public Namespace getNamespace() {
        return namespace;
    }

    /**
     ** Get the associated scope.
     **
     ** @return     return the scope
     **/
    public Scope getScope() {
        return scope;
    }

    /**
     ** Get the associated content store.
     **
     ** @return     return the content store
     **/
    public XContentStore getContentStore() {
        return (XContentStore)contentStore;
    }

    /**
     ** Get the associated descriptors store.
     **
     ** @return     return the descriptors store
     **/
    public XDescriptorsStore getDescriptorsStore() {
        return (XDescriptorsStore)nodeStore;
    }

    /**
     ** Returns true if this store is in repair mode (i.e. read_only).
     ** @return     true if this store is in repair mode
     **/
    public boolean isInRepairMode() {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "isInRepairMode" );

        boolean result = repairMode;

        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "isInRepairMode", new Boolean(result) );
        return result;
    }

    /**
     ** Set repair mode for this store.
     ** @param     onOpenTA 0=WAIT, 1=ERROR, 2=ROLLBACK
     ** @param     waitTimeout the timeout for onOpenTA=0 (WAIT)
     ** @exception XException xdav error
     **/
    public void setRepairMode( int onOpenTA, long waitTimeout ) throws XException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "setRepairMode", new Object[] {new Integer(onOpenTA), new Long(waitTimeout)}  );

        if( !(nodeStore instanceof XUnavailableStore) ) {
            getDescriptorsStore().setRepairMode( onOpenTA, waitTimeout );
            getContentStore().setRepairMode( onOpenTA, waitTimeout );
            this.repairMode = true;
        }

        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "setRepairMode" );
    }

    /**
     ** Release repair mode for this handler.
     ** @exception XException xdav error
     **/
    public void releaseRepairMode() throws XException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "releaseRepairMode" );

        if( !(nodeStore instanceof XUnavailableStore) ) {
            getDescriptorsStore().releaseRepairMode();
            getContentStore().releaseRepairMode();
            this.repairMode = false;
        }

        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "releaseRepairMode" );
    }

    /**
     ** Get the associated DB handler.
     **
     ** @return     return value explanation
     **/
    public XDbHandler getDbHandler() {
        return dbHandler;
    }

    /**
     * Initializes descriptors store.
     * @param token namespace access token
     * @exception ServiceInitializationFailedException Throws an exception
     * if the descriptors store has already been initialized before
     */
    public void initialize(NamespaceAccessToken token)
        throws ServiceInitializationFailedException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "initialize", new Object[]{token} );

        if( initialized )
            throw new ServiceInitializationFailedException( this, "Already initialized" );

//      String expressionFactoryClass = (String)parameters.get
//            (IBasicExpressionFactory.BASIC_EXPRESSION_FACTORY_CLASS);
//
//        if (expressionFactoryClass == null) {
//            parameters.put (IBasicExpressionFactory.BASIC_EXPRESSION_FACTORY_CLASS,
//                            XGlobals.DEFAULT_EXPRESSION_FACTORY);
//        }

        String queryClass = (String)parameters.get
            (BasicSearchLanguage.BASIC_QUERY_CLASS);

        if (queryClass == null) {
            parameters.put (BasicSearchLanguage.BASIC_QUERY_CLASS,
                            XGlobals.DEFAULT_QUERY);
        }


        this.nsaToken = token;

        // register the tamino namespace so the Webdav layer uses the right prefix
        NamespaceCache.getNamespace(TAMINO_NAMESPACE_PREFIX, TAMINO_NAMESPACE_URI);

        // monitoring variables
        this.monName = super.getName();
        // this.monParent = ...; no parent monitorable
        this.monChildren.add( contentStore );
        this.monChildren.add( nodeStore );
        this.monChildren.add( dbHandler );
        this.monitor = Monitor.getMonitor( this ); // this comes last!!

        // monitoring items
        monitor.registerProperty (TAMINO_URL);
        monitor.registerProperty (REPAIR_MODE);
        createContentCounter = monitor.getCounter( CREATE_CONTENT_COUNTER );
        createContentTimer = monitor.getTimer( CREATE_CONTENT_TIMER );
        retrieveContentCounter = monitor.getCounter( RETRIEVE_CONTENT_COUNTER );
        retrieveContentTimer = monitor.getTimer( RETRIEVE_CONTENT_TIMER );
        storeContentCounter = monitor.getCounter( STORE_CONTENT_COUNTER );
        storeContentTimer = monitor.getTimer( STORE_CONTENT_TIMER );
        removeContentCounter = monitor.getCounter( REMOVE_CONTENT_COUNTER );
        removeContentTimer = monitor.getTimer( REMOVE_CONTENT_TIMER );
        createObjectCounter = monitor.getCounter( CREATE_OBJ_COUNTER );
        createObjectTimer = monitor.getTimer( CREATE_OBJ_TIMER );
        retrieveObjectCounter = monitor.getCounter( RETRIEVE_OBJ_COUNTER );
        retrieveObjectTimer = monitor.getTimer( RETRIEVE_OBJ_TIMER );
        storeObjectCounter = monitor.getCounter( STORE_OBJ_COUNTER );
        storeObjectTimer = monitor.getTimer( STORE_OBJ_TIMER );
        removeObjectCounter = monitor.getCounter( REMOVE_OBJ_COUNTER );
        removeObjectTimer = monitor.getTimer( REMOVE_OBJ_TIMER );
        prepareCounter = monitor.getCounter( PREPARE_COUNTER );
        prepareTimer = monitor.getTimer( PREPARE_TIMER );
        commitCounter = monitor.getCounter( COMMIT_COUNTER );
        commitTimer = monitor.getTimer( COMMIT_TIMER );
        rollbackCounter = monitor.getCounter( ROLLBACK_COUNTER );
        rollbackTimer = monitor.getTimer( ROLLBACK_TIMER );

        // init the children
        // child stores are initialized from Slide; only dbHandler to be initialized
        dbHandler.initialize();
        initialized = true;

        try {
            super.initialize(token); // this comes after monitoring stuff !!
        }
        catch( ServiceInitializationFailedException x ) {
            if( logger.isLoggable(Level.WARNING) )
                logger.warning( "Store "+getName()+
                                   " could not be initialized. Reason: "+x.getMessage() );

            //reset resourceManagers
            resourceManagers = new Service[0];

            //set unavailable childstore
            XUnavailableStore unavailChildStore = new XUnavailableStore( x.getMessage() );
            setNodeStore( unavailChildStore );
            setSecurityStore( unavailChildStore );
            setLockStore( unavailChildStore );
            setRevisionDescriptorsStore( unavailChildStore );
            setRevisionDescriptorStore( unavailChildStore );
            setContentStore( unavailChildStore );

            // init the new childstore
            unavailChildStore.initialize( token );
            unavailChildStore.setScope( scope );
            unavailChildStore.setNamespace( namespace );
            try {
                unavailChildStore.setParameters( parameters );
            }
            catch( ServiceParameterErrorException x1 ) {}
            catch( ServiceParameterMissingException x1 ) {}
        }

        // Check repair mode token
        XAdministrationStore adminStore = (XAdministrationStore)namespace.getStore( new Scope(REPAIRER_URI) );
        if( adminStore != null && adminStore.isInitialized() ) {
            XRepairerStore repairerStore = (XRepairerStore)adminStore.getChildStore();

            if( repairerStore.existsRepairModeToken(getName()) ) {
                if( CLEAR_REPAIR_MODE_TOKEN_ON_INIT ) {
                    repairerStore.removeRepairModeToken( getName() );
                }
                else {
                    try {
                        setRepairMode( ON_OPEN_TA_WAIT, ON_OPEN_TA_WAIT_TIMEOUT );
                    }
                    catch( XException x ) {
                        throw new ServiceInitializationFailedException( this, x );
                    }
                }
            }
        }

        // WAM enable this when overriding the BasicExpressions
        //        parameters.put (BasicExpressionFactory.EXPRESSION_FACTORY,
        //                        "org.apache.slide.store.tamino.datastore.search.XBasicExpressionFactory");

        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "initialize" );
    }

    /**
     * Initializes this child store with a set of parameters.
     * These are:
     * <li>taminoBase (the base of the Tamino URL, e.g. http://myhost/tamino
     * <li>database (the Tamino database, i.e. mydb)
     * <li>collection (the Tamino collection, i.e. mycoll)
     *
     * @param parameters Hashtable containing the parameters' name
     * and associated value
     *
     * @exception   ServiceParameterErrorException
     * @exception   ServiceParameterMissingException
     */
    public void setParameters(Hashtable parameters)
        throws ServiceParameterErrorException, ServiceParameterMissingException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "setParameters", new Object[] {parameters} );

        super.setParameters( parameters );
        this.parameters = parameters;

        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "setParameters" );
    }

    //-------------------------------------------------------------
    // ISlideAccessor interface
    // ------------------------------------------------------------

    /**
     * Get the Domain parameter (from Domain.xml) given by name.
     *
     * @param  name    the parameter name
     * @return the parameter value
     */
    public String getParameter( String name ) {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "getParameter", new Object[] {name} );

        String result = (String) parameters.get( name );

        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "getParameter", result );
        return result;
    }

    public boolean useBinding() {
        return XStore.getBindingBoolean(getParameter(XGlobals.USE_BINDING));
    }
    
    /**
     * Get the associated namespace access token.
     * @return     the namespace access token
     */
    public NamespaceAccessToken getNamespaceAccessToken() {
        return nsaToken;
    }
    
    public String getNamespaceName() {
        return nsaToken.getName();
    }

    //-------------------------------------------------------------
    // IMonitorable interface
    // ------------------------------------------------------------

    /**
     ** Return the name of this monitorable.
     **
     ** @return the name
     **
     **/
    public String getMonName() {
        //return super.getName();
        return monName;
    }

    /**
     ** Get the parent monitorable.
     **
     ** @return the parent, null if none
     **
     **/
    public IMonitorable getMonParent() {
        return monParent;
    }

    /**
     ** Get child monitorables.
     **
     ** @return  the children
     **
     **/
    public List getMonChildren() {
        return monChildren;
    }

    /**
     ** Get the Monitor object.
     **
     ** @return the monitor object
     **
     **/
    public IMonitor getMonitor() {
        return monitor;
    }


    /**
     ** Get the value of a registered property.
     ** @param prop property name
     ** @return the value of the indicated property as String
     **
     **/
    public Object getMonProperty( String prop ) {
        Object result = null;

        if (TAMINO_URL.equals (prop) ) {
            result = getParameter( TAMINO_BASE )+XUri.SEP+
                getParameter( TAMINO_DATABASE )+XUri.SEP+
                getParameter( TAMINO_COLLECTION );
        }
        else if (REPAIR_MODE.equals (prop) ) {
            result = String.valueOf( repairMode );
        }
        return result;
    }

    // Inherited from org.apache.slide.store.StandardStore
    public String toString () {
        String def = super.toString();
        return (getName() != null ? getName() : def);
    }
}


