/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/store/org/apache/slide/store/tamino/common/XContentHandler.java,v 1.4 2005/01/19 15:19:02 pnever Exp $
 * $Revision: 1.4 $
 * $Date: 2005/01/19 15:19:02 $
 *
 * ====================================================================
 *
 * 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.common;

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 org.apache.slide.content.NodeRevisionContent;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.store.tamino.datastore.IDbSession;
import org.apache.slide.store.tamino.datastore.XDbHandler;
import org.apache.slide.store.tamino.store.ISlideAccessor;
import org.apache.slide.store.tamino.store.XContentStore;
import org.apache.slide.store.tamino.store.monitoring.AbstractMonitorable;
import org.apache.slide.store.tamino.store.monitoring.IMonitorable;
import org.apache.slide.store.tamino.store.monitoring.Monitor;
import org.apache.slide.util.ClassName;
import org.apache.slide.util.XException;

/**
 *
 * @author    martin.wallmer@softwareag.com
 *
 * @version   $Revision: 1.4 $
 *
 */
public class XContentHandler extends AbstractMonitorable
implements IContentHandler, XGlobals {

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

    /** An instance of XContentStore for calling back. */
    private ISlideAccessor childStore;

    /** The content cache. */
    private IContentCache contentCache;

    /** Content caching enabled? */
    private boolean contentCachingEnabled = false;


    /**
     * Constructs the handler
     *
     * @pre        (descriptors != null)
     *
     * @param      childStore the calling instance
     *
     */
    public XContentHandler (ISlideAccessor childStore) {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "<init>", new Object[]{childStore} );

        this.childStore = childStore;
        long contentCacheMaxEntrySize = 0;

        try {
            contentCacheMaxEntrySize =
                (new Long(childStore.getParameter(CC_MAX_ESIZE)).longValue());
        }
        catch( NumberFormatException x ) {
            contentCacheMaxEntrySize = CC_MAX_ESIZE_DEFAULT;
        }

        this.contentCache = new XContentCache( contentCacheMaxEntrySize );
        this.contentCachingEnabled = (contentCacheMaxEntrySize > 0);

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

    /**
     * Initializes this component.
     */
    public void initialize() {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "initialize" );

        // monitoring variables
        this.monName = "ContentHandler";
        this.monParent = (IMonitorable) childStore;
        this.monChildren.add( contentCache );
        ((AbstractMonitorable)contentCache).setParent(this);
        this.monitor = Monitor.getMonitor( this ); // this comes last!!

        // init the children
        contentCache.initialize();

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

    /**
     * Creates a NodeRevisionContent object within datastore.
     *
     * @pre        (nrc != null)
     *
     * @param      nrc the NodeRevisionContent object to be made persistent
     * @param      nrd the NodeRevisionDescriptor object to be made persistent
     *
     * @return     the IContent object wrapping the NodeRevisionContent object
     *
     * @exception  XException when underlying service reports an error
     */
    public IContent create (NodeRevisionContent nrc, NodeRevisionDescriptor nrd)
    throws XException {
        if (logger.isLoggable (Level.FINE))
            logger.entering (CLASSNAME, "create", new Object[] {nrc});

        IContent content = new XContent (nrc, nrd);

        IDbSession dbSession = getDbSession();
        IContent result = dbSession.createContent (content);

        if( contentCachingEnabled ) {
            contentCache.add( result );
        }

        if (logger.isLoggable(Level.FINE))
            logger.exiting (CLASSNAME, "nrc", result);

        return result;
    }

    /**
     * Retrieves an IContent object from the datastore.
     *
     * @pre        contentId is unique within datastore
     *
     * @param      contentId that identifies the content within datastore
     *
     * @return     the IContent object wrapping the NodeRevisionContent object
     *
     * @exception  XException when underlying service reports an error
     */
    public IContent read (String contentId) throws XException {
        if (logger.isLoggable (Level.FINE))
            logger.entering (CLASSNAME, "read", new Object[] {contentId});

        IDbSession dbSession = getDbSession();
        IContent result = null;

        if( contentCachingEnabled ) {
            result = contentCache.get( contentId );

            if( result == null ) {
                result = dbSession.readContent (contentId);
                contentCache.add( result );
            }
        }
        else {
            result = dbSession.readContent (contentId);
        }

        if (logger.isLoggable(Level.FINE))
            logger.exiting (CLASSNAME, "read", result);

        return result;
    }

    /**
     * Updates a NodeRevisionContent object within datastore.
     *
     * @pre        (nrc != null)
     * @pre         contentId exists in datastore
     *
     * @param      contentId identifying the object in datastore
     * @param      nrc the NodeRevisionContent object to be updated within datastore
     * @param      nrd the NodeRevisionDescriptor object to be updated within datastore
     *
     * @return     the IContent object wrapping the NodeRevisionContent object
     *
     * @exception  XException when underlying service reports an error
     */
    public IContent update (String contentId, NodeRevisionContent nrc,
    NodeRevisionDescriptor nrd) throws XException {
        if (logger.isLoggable (Level.FINE))
            logger.entering (CLASSNAME, "update", new Object[] {contentId});

        IDbSession dbSession = getDbSession();
        IContent content = new XContent (nrc, nrd);
        IContent result = dbSession.updateContent (content, contentId);

        if( contentCachingEnabled ) {
            contentCache.add( result );
        }

        if (logger.isLoggable(Level.FINE))
            logger.exiting (CLASSNAME, "update", result);

        return result;
    }

    /**
     * Deletes a NodeRevisionContent object from datastore.
     *
     * @pre         contentId exists in datastore
     *
     * @param      contentId identifying the object in datastore
     *
     * @exception  XException when underlying service reports an error
     */
    public void delete (String contentId) throws XException {
        if (logger.isLoggable (Level.FINE))
            logger.entering (CLASSNAME, "delete", new Object[] {contentId});

        getDbHandler().getDbSession().deleteContent (contentId);

        if( contentCachingEnabled ) {
            contentCache.remove( contentId );
        }

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


    /**
     * called in two phase commit context.
     *
     ** @exception XException
     */
    public void prepare () throws XException {
        if (logger.isLoggable (Level.FINE))
            logger.entering (CLASSNAME, "prepare");

        getDbHandler().getDbSession().prepare();

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

    }

    /**
     * <p>Commit the global transaction associated to the current thread.
     * <p>If database session is still available: commit database and clean-up session.
     * <p>Clean-up delta cache: remove entries marked deleted; call commitEvent for
     * non-deleted entries and move them to the global cache; remove delta cache.
     *
     * @param      onePhase   If true, a one-phase commit protocol should be used to commit
     *                        the work done within the current thread.
     *
     * @exception  XException
     */
    public void commit (boolean onePhase) throws XException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "commit", new Object[] {new Boolean(onePhase)} );

        IDbSession dbSession = null;
        try {
            dbSession = getDbHandler().getDbSession();
            if( dbSession != null ) {
                // commit and remove db session
                dbSession.commit(onePhase);
            }
        }
        finally {
            if (dbSession != null) {
                getDbHandler().removeDbSession();
            }
        }
    }
    
    /**
     * <p>Roll back work done on behalf of a transaction branch.
     * <p>If database session is still available: rollback database and clean-up session.
     * <p>Clean-up delta cache: remove all entries; remove delta cache.
     *
     * @exception  XException
     */
    public void rollback() throws XException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "rollback" );

        IDbSession dbSession = getDbHandler().getDbSession();
        try {
            if( dbSession != null ) {
                dbSession.rollback();
            }
        }
        finally {
            contentCache.removeAll();

            if( dbSession != null ) {
                getDbHandler().removeDbSession();
            }
        }


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

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

        boolean result = ((XContentStore)childStore).isInRepairMode();

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

    /**
     ** Set repair mode for this handler.
     ** @param     onOpenTA 0=WAIT, 1=ERROR, 2=ROLLBACK
     ** @param     waitTimeout the timeout for onOpenTA=0 (WAIT)
     ** @exception XException
     **/
    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( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "setRepairMode" );
    }

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

        if( isInRepairMode() ) {
            contentCache.removeAll();
        }

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


    /**
     ** Get the associated DB handler.
     **
     ** @return     return the DB handler
     **/
    private XDbHandler getDbHandler() {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "getDbHandler" );

        XDbHandler dbHandler = ((XContentStore)childStore).getDbHandler();

        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "getDbHandler", dbHandler );
        return dbHandler;
    }

    /**
     * Get the database session.
     * @exception XException
     */
    private IDbSession getDbSession() throws XException {
        IDbSession dbSession = getDbHandler().getDbSession();

        if( dbSession == null ) {
            dbSession = getDbHandler().createDbSession( );

            if( logger.isLoggable(Level.FINE) )
                logger.fine(CLASSNAME, "dbSession", "Created DB session for "+childStore);
        }
        return dbSession;
    }
}
