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

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.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import org.apache.slide.common.NamespaceAccessToken;
import org.apache.slide.common.ServiceAccessException;
import org.apache.slide.common.ServiceInitializationFailedException;
import org.apache.slide.common.Uri;
import org.apache.slide.content.NodeRevisionContent;
import org.apache.slide.content.NodeRevisionDescriptor;
import org.apache.slide.content.NodeRevisionDescriptors;
import org.apache.slide.content.NodeRevisionNumber;
import org.apache.slide.content.RevisionDescriptorNotFoundException;
import org.apache.slide.content.RevisionNotFoundException;
import org.apache.slide.macro.ForbiddenException;
import org.apache.slide.store.tamino.common.XForbiddenException;
import org.apache.slide.store.tamino.common.XGlobals;
import org.apache.slide.store.tamino.jdomobjects.XFactory;
import org.apache.slide.store.tamino.jdomobjects.XNodeRevisionDescriptor;
import org.apache.slide.store.tamino.store.XMemoryStore;
import org.apache.slide.store.tamino.store.monitoring.Monitor;
import org.apache.slide.store.tamino.tools.stores.XDomainFileHandler;
import org.apache.slide.store.tamino.tools.stores.XNamespace;
import org.apache.slide.store.tamino.tools.stores.XStore;
import org.apache.slide.store.tamino.tools.stores.XStoreGroup;
import org.apache.slide.store.tamino.tools.stores.XStoreType;
import org.apache.slide.structure.ObjectNode;
import org.apache.slide.structure.ObjectNotFoundException;
import org.apache.slide.structure.SubjectNode;
import org.apache.slide.util.ClassName;
import org.apache.slide.util.JDom;
import org.apache.slide.util.Pattern;
import org.apache.slide.util.Strings;
import org.apache.slide.util.XUri;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;


/**
 * Tamino-based implementation of DescriptorsStore.
 *
 * @author    Hardy.Kiefer@softwareag.com
 *
 * @version   0.1
 *
 */
public class XMonitoringStore
    extends XMemoryStore
    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);

    private XNamespace ns;
    private XMLOutputter xmlout = null;
    
    
    /**
     ** Get the XML content for the given .
     **
     ** @pre        true
     ** @post       true
     **
     ** @param
     ** @return     result
     **
     ** @exception
     **/
    private String getXmlContent(Uri uri) {
        XUri uh;
        String template;
        XStoreGroup group;
        String fileName;
        final Pattern monUh;
        final Element elm;
        IMonitorable monitorable;
            
        uh = new XUri( uri.getRelative() );
        if (!uh.isXmlFile() || uh.size() > 2) {
            return null;
        }
        if (uh.size() == 1) {
            fileName = Strings.stripExtension(uh.lastSegment());
            template = (String) parameters.get(fileName);
            monitorable = Monitor.getMonitored(template);
            if (monitorable == null) {
                return null;
            }
            elm = monitorable.getMonitor().snapshotElement();
        } else {
            fileName = Strings.stripExtension(uh.lastSegment());
            template = (String) parameters.get(fileName);
            if (template == null) {
                return null;
            }
            group = ns.lookupStoreGroup(uh.firstSegment());
            if (group == null) {
                return null;
            }
            monUh = new Pattern(template);
            elm = new Element("store");
            group.forAll(new XStoreGroup.Visitor() {
                        public void visit(XStoreType type, XStore store) {
                            snapshot(elm, monUh, store, type.name);
                        }
                    });
        }
        return xmlout.outputString( new Document( elm ) );
    }
    
    private void snapshot(Element elm, Pattern monUh, XStore store, String typeName) {
        String uri;
        IMonitorable monitorable;
        Element snapshot;
        
        uri = monUh.resolve(store.getName());
        monitorable = Monitor.getMonitored( uri );
        if (monitorable == null) {
            return;
        }
        snapshot = new Element(typeName);
        snapshot.addContent(monitorable.getMonitor().snapshotElement());
        elm.addContent(snapshot);
    }
    
    // Inherited from XMemoryStore
    public synchronized void initialize( NamespaceAccessToken token )
        throws ServiceInitializationFailedException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "initialize",
                                                            new Object[] {(token!=null ? token.getName() : null)} );
        
        super.initialize( token );
        
        try {
            this.ns = XDomainFileHandler.get().getDomain().getNamespace(token.getName());
            this.xmlout = JDom.outputter();
        }
        catch( Exception x ) {
            throw new ServiceInitializationFailedException( this, x );
        }
        
        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "initialize" );
    }
    
    /**
     * Retrieve revision content.
     *
     * @param uri Uri
     * @param revisionDescriptor the revision descriptor
     * @return the revision content
     * @exception ServiceAccessException service access exception
     * @exception RevisionNotFoundException if revision was not found
     */
    public NodeRevisionContent retrieveRevisionContent(
        Uri uri, NodeRevisionDescriptor revisionDescriptor)
        throws ServiceAccessException, RevisionNotFoundException {
        if( logger.isLoggable(Level.FINE) )
            logger.entering( CLASSNAME, "retrieveRevisionContent", new Object[] {uri,
                            (revisionDescriptor!=null
                                 ? revisionDescriptor.getRevisionNumber()+"@"+revisionDescriptor.getBranchName()
                                 : null) } );
        
        String uriStr = uri.toString();
        
        // Construct the revision content:
        String xmlcontent = getXmlContent( uri );
        if( xmlcontent == null ) {
            throw new RevisionNotFoundException(
                uriStr, revisionDescriptor.getRevisionNumber() );
        }
        
        NodeRevisionContent rc = new NodeRevisionContent();
        rc.setContent( xmlcontent.toCharArray() );
        // update content length
        revisionDescriptor.setContentLength(xmlcontent.length());
        revisionDescriptor.setLastModified( new Date() );
        
        descriptor.put( uriStr+"-1.0", revisionDescriptor );  // XMemoryStore is version aware
        
        NodeRevisionContent result = rc;
        
        if( logger.isLoggable(Level.FINE) )
            logger.exiting( CLASSNAME, "retrieveRevisionContent", result );
        return result;
    }
    
    /**
     * Retrieve an object from the Descriptors Store.
     *
     * @param uri Uri of the object we want to retrieve
     * @return the object from the Descriptors Store.
     * @exception ServiceAccessException Error accessing the Descriptors Store
     * @exception ObjectNotFoundException The object to retrieve was not found
     */
    public ObjectNode retrieveObject(Uri uri)
        throws ServiceAccessException, ObjectNotFoundException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "retrieveObject",
                                                            new Object[] {uri.toString()} );
        
        String uriStr = uri.toString();
        Object tempObject = objects.get(uriStr);
        if (tempObject == null) {
            // unknown object
            if (uriStr.startsWith(scope.toString())) {
                String relativeUri = uri.getRelative();
                Vector    children = new Vector();
                String  xmlcontent = "";
                
                if ( Strings.endsWithIgnoreCase(relativeUri, XUri.XML_SUFF) ) {
                    //create a file with the monitored values:
                    
                    // Construct the revision descriptor and descriptors objects:
                    NodeRevisionDescriptor rd = XFactory.createMemberNRD( uriStr, "text/xml" );
                    NodeRevisionDescriptors rds = XFactory.createNRDs( uriStr );
                    
                    // Construct the revision content:
                    xmlcontent = getXmlContent( uri );
                    if( xmlcontent == null )
                        throw new ObjectNotFoundException(uri);
                    
                    NodeRevisionContent rc = new NodeRevisionContent();
                    rc.setContent( xmlcontent.toCharArray() );
                    // update content length
                    rd.setContentLength(xmlcontent.length());
                    
                    descriptors.put( uriStr, rds );
                    descriptor.put( uriStr+"-1.0", rd );  // XMemoryStore is version aware
                }
                else {
                    // create a folder of a monitorable child:
                    if ( relativeUri.length() == 0 ) {
                        //get root
                        relativeUri+= "/";
                        List stores = Collections.EMPTY_LIST;
                        stores = ns.getPublicStoreGroups();
                        Iterator st = stores.iterator();
                        while( st.hasNext() )
                            children.add( uriStr+XUri.SEP+((XStoreGroup) st.next()).name );
                        // add "global" file on root level:
                        Enumeration e = parameters.keys();
                        while ( e.hasMoreElements() ) {
                            String fileName = (String)e.nextElement();
                            if ( "/".equals( (String)parameters.get( fileName ))) {
                                children.add( uriStr + "/" + fileName + XUri.XML_SUFF );
                            }
                        }
                    }
                    else { // relativeUri.length() != 0
                        // Construct the revision descriptor and descriptors objects if necessary:
                        NodeRevisionDescriptors rds = (NodeRevisionDescriptors)descriptors.get( uriStr );
                        NodeRevisionDescriptor rd = (NodeRevisionDescriptor)descriptor.get( uriStr+"-1.0" );
                        if( rds == null || rd == null ) {
                            rd = XFactory.createCollectionNRD( uriStr );
                            rds = XFactory.createNRDs( uriStr );
                            descriptors.put( uriStr, rds );
                            descriptor.put( uriStr+"-1.0", rd );  // XMemoryStore is version aware
                        }
                        
                        // get snapshot files of monitorables:
                        Enumeration e = parameters.keys();
                        while ( e.hasMoreElements() ) {
                            String fileName = (String)e.nextElement();
                            if (  !fileName.equals("basicQueryClass")   &&
                                !fileName.equals( "stylesheet" )      &&
                                ! "/".equals( (String)parameters.get( fileName ))) {
                                children.add( uriStr + "/" + fileName + XUri.XML_SUFF);
                            }
                        }
                    }
                }
                tempObject = new SubjectNode( uriStr, children, new Vector() );
                objects.put( uriStr, (ObjectNode)tempObject );
            }
            else {
                throw new ObjectNotFoundException(uri);
            }
        }
        ObjectNode result = ((ObjectNode) tempObject).cloneObject();
        
        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "retrieveObject",
                                                               (result!=null ? result.getUri() : null) );
        return result;
    }
    
    /**
     * Overwritten to forbid structural modifications.
     *
     * @param uri Uri of the object we want to update
     * @param object Object to update
     * @exception ServiceAccessException Error accessing the Descriptors Store
     * @exception ObjectNotFoundException The object to update was not found
     */
    public void storeObject(Uri uri, ObjectNode object)
        throws ServiceAccessException, ObjectNotFoundException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME, "storeObject",
                                                            new Object[] {uri, (object!=null ? object.getUri() : null)} );
        
        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME, "storeObject" );
        
        //        throw new ServiceAccessException(this, "Operation forbidden in scope "+scope);
        throw new ServiceAccessException(this,
                                         new ForbiddenException(uri.toString(), new XForbiddenException( "Invalid uri: "+uri.toString() )) );
    }
    
    /**
     * Retrieve revision descriptor.
     *
     * @param uri uri
     * @param revisionNumber Node revision number
     * @return the revision descriptor
     * @exception ServiceAccessException Service access error
     * @exception RevisionDescriptorNotFoundException if revision descriptor was not found
     */
    public NodeRevisionDescriptor retrieveRevisionDescriptor
        (Uri uri, NodeRevisionNumber revisionNumber)
        throws ServiceAccessException, RevisionDescriptorNotFoundException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME,
                                                            "retrieveRevisionDescriptor", new Object[] {uri, revisionNumber} );
        
        String uriStr = uri.toString();
        NodeRevisionDescriptor result = null;
        NodeRevisionDescriptor rd = (NodeRevisionDescriptor) descriptor.get(uriStr+"-1.0");  // XMemoryStore is version aware
        if( rd == null ) {
            throw new RevisionDescriptorNotFoundException(uriStr);
        }
        if( !XNodeRevisionDescriptor.isCollection(rd) ) {
            //update getlastmodified property
            rd.setLastModified(new Date());
        }
        result = rd.cloneObject();
        
        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME,
                                                           "retrieveRevisionDescriptor", (result!=null
                                                                                              ? result.getRevisionNumber()+"@"+result.getBranchName()
                                                                                              : null) );
        return result;
    }
    
    /**
     * Remove revision descriptor.
     *
     * @param uri Uri
     * @param number Revision number
     * @exception ServiceAccessException Service access error
     */
    public void removeRevisionDescriptor(Uri uri, NodeRevisionNumber number)
        throws ServiceAccessException {
        if( logger.isLoggable(Level.FINE) ) logger.entering( CLASSNAME,
                                                            "removeRevisionDescriptor", new Object[] {uri, number} );
        
        if( logger.isLoggable(Level.FINE) ) logger.exiting( CLASSNAME,
                                                           "removeRevisionDescriptor" );
        
        //        throw new ServiceAccessException(this, "Operation forbidden in scope "+scope);
        throw new ServiceAccessException(this,
                                         new ForbiddenException(uri.toString(), new XForbiddenException( "Invalid uri: "+uri.toString() )) );
    }
}

