Package org.exist.xquery.update

Source Code of org.exist.xquery.update.Modification

/*
*  eXist Open Source Native XML Database
*  Copyright (C) 2001-2010 The eXist Project
*  http://exist-db.org
*
*  This program is free software; you can redistribute it and/or
*  modify it under the terms of the GNU Lesser General Public License
*  as published by the Free Software Foundation; either version 2
*  of the License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public
*  License along with this library; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*
*  $Id$
*/
package org.exist.xquery.update;

import java.util.Iterator;

import org.apache.log4j.Logger;
import org.exist.EXistException;
import org.exist.collections.Collection;
import org.exist.collections.triggers.DocumentTrigger;
import org.exist.collections.triggers.DocumentTriggers;
import org.exist.collections.triggers.TriggerException;
import org.exist.dom.DefaultDocumentSet;
import org.exist.dom.DocumentImpl;
import org.exist.dom.DocumentSet;
import org.exist.dom.MutableDocumentSet;
import org.exist.dom.NodeIndexListener;
import org.exist.dom.NodeProxy;
import org.exist.dom.StoredNode;
import org.exist.memtree.DocumentBuilderReceiver;
import org.exist.memtree.MemTreeBuilder;
import org.exist.security.PermissionDeniedException;
import org.exist.storage.DBBroker;
import org.exist.storage.StorageAddress;
import org.exist.storage.lock.Lock;
import org.exist.storage.serializers.Serializer;
import org.exist.storage.txn.TransactionException;
import org.exist.storage.txn.TransactionManager;
import org.exist.storage.txn.Txn;
import org.exist.util.LockException;
import org.exist.util.hashtable.Int2ObjectHashMap;
import org.exist.xquery.*;
import org.exist.xquery.value.Item;
import org.exist.xquery.value.NodeValue;
import org.exist.xquery.value.Sequence;
import org.exist.xquery.value.SequenceIterator;
import org.exist.xquery.value.Type;
import org.exist.xquery.value.ValueSequence;
import org.w3c.dom.DOMException;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

/**
* @author wolf
*
*/
public abstract class Modification extends AbstractExpression
{

  protected final static Logger LOG =
    Logger.getLogger(Modification.class);
 
  protected final Expression select;
  protected final Expression value;
 
  protected DocumentSet lockedDocuments = null;
  protected MutableDocumentSet modifiedDocuments = new DefaultDocumentSet();
    protected Int2ObjectHashMap<DocumentTrigger> triggers;
   
    /**
   * @param context
   */
  public Modification(XQueryContext context, Expression select, Expression value) {
    super(context);
    this.select = select;
    this.value = value;
        this.triggers = new Int2ObjectHashMap<DocumentTrigger>(10);
    }

  public int getCardinality() {
    return Cardinality.EMPTY;
  }
 
  /* (non-Javadoc)
   * @see org.exist.xquery.AbstractExpression#returnsType()
   */
  public int returnsType() {
    return Type.EMPTY;
  }

  /* (non-Javadoc)
   * @see org.exist.xquery.AbstractExpression#resetState()
   */
  public void resetState(boolean postOptimization) {
    super.resetState(postOptimization);
    select.resetState(postOptimization);
    if (value != null)
      {value.resetState(postOptimization);}
  }

    @Override
    public void accept(ExpressionVisitor visitor) {
        select.accept(visitor);
        if (value != null) {
            value.accept(visitor);
        }
    }

  /* (non-Javadoc)
   * @see org.exist.xquery.Expression#analyze(org.exist.xquery.Expression, int)
   */
  public void analyze(AnalyzeContextInfo contextInfo) throws XPathException {
    contextInfo.setParent(this);
    contextInfo.addFlag(IN_UPDATE);
    select.analyze(contextInfo);
    if (value != null)
      {value.analyze(contextInfo);}
  }
 
  /**
   * Acquire a lock on all documents processed by this modification.
   * We have to avoid that node positions change during the
   * operation.
   *
   * @param nodes
   *
   * @throws LockException
   * @throws TriggerException
   */
  protected StoredNode[] selectAndLock(Txn transaction, Sequence nodes) throws LockException, PermissionDeniedException,
    XPathException, TriggerException {
      final Lock globalLock = context.getBroker().getBrokerPool().getGlobalUpdateLock();
      try {
          globalLock.acquire(Lock.READ_LOCK);
        
          lockedDocuments = nodes.getDocumentSet();
         
        // acquire a lock on all documents
          // we have to avoid that node positions change
          // during the modification
          lockedDocuments.lock(context.getBroker(), true, false);
         
        final StoredNode ql[] = new StoredNode[nodes.getItemCount()];
      for (int i = 0; i < ql.length; i++) {
                final Item item = nodes.itemAt(i);
                if (!Type.subTypeOf(item.getType(), Type.NODE))
                    {throw new XPathException(this, "XQuery update expressions can only be applied to nodes. Got: " +
                        item.getStringValue());}
                final NodeValue nv = (NodeValue)item;
                if (nv.getImplementationType() == NodeValue.IN_MEMORY_NODE)
                    {throw new XPathException(this, "XQuery update expressions can not be applied to in-memory nodes.");}
                final Node n = nv.getNode();
                if (n.getNodeType() == Node.DOCUMENT_NODE)
                    {throw new XPathException(this, "Updating the document object is not allowed.");}
        ql[i] = (StoredNode) n;
        final DocumentImpl doc = (DocumentImpl)ql[i].getOwnerDocument();
        //prepare Trigger
        prepareTrigger(transaction, doc);
      }
      return ql;
      } finally {
          globalLock.release(Lock.READ_LOCK);
      }
  }
 
  protected Sequence deepCopy(Sequence inSeq) throws XPathException {
    context.pushDocumentContext();
    final MemTreeBuilder builder = context.getDocumentBuilder();
    final DocumentBuilderReceiver receiver = new DocumentBuilderReceiver(builder);
    final Serializer serializer = context.getBroker().getSerializer();
    serializer.setReceiver(receiver);
   
    try {
      final Sequence out = new ValueSequence();
      for (final SequenceIterator i = inSeq.iterate(); i.hasNext(); ) {
        Item item = i.nextItem();
        if (item.getType() == Type.DOCUMENT) {
          if (((NodeValue)item).getImplementationType() == NodeValue.PERSISTENT_NODE) {
            final StoredNode root = (StoredNode) ((NodeProxy)item).getDocument().getDocumentElement();
            item = new NodeProxy(root.getDocument(), root.getNodeId(), root.getInternalAddress());
          } else {
            item = (Item)((NodeValue) item).getOwnerDocument().getDocumentElement();
          }
        }
        if (Type.subTypeOf(item.getType(), Type.NODE)) {
          if (((NodeValue)item).getImplementationType() == NodeValue.PERSISTENT_NODE) {
            final int last = builder.getDocument().getLastNode();
            final NodeProxy p = (NodeProxy) item;
            serializer.toReceiver(p, false, false);
                        if (p.getNodeType() == Node.ATTRIBUTE_NODE)
                            {item = builder.getDocument().getLastAttr();}
                        else
                            {item = builder.getDocument().getNode(last + 1);}
          } else {
            ((org.exist.memtree.NodeImpl)item).deepCopy();
          }
        }
        out.add(item);
      }
      return out;
        } catch(final SAXException e) {
            throw new XPathException(this, e.getMessage(), e);
    } catch (final DOMException e) {
        throw new XPathException(this, e.getMessage(), e);
    } finally {
      context.popDocumentContext();
    }
  }

    protected void finishTriggers(Txn transaction) throws TriggerException {
        final Iterator<DocumentImpl> iterator = modifiedDocuments.getDocumentIterator();
   
        while(iterator.hasNext()) {
          final DocumentImpl doc = iterator.next();
            context.addModifiedDoc(doc);
      finishTrigger(transaction, doc);
    }
       
        triggers.clear();
    }

    /**
   * Release all acquired document locks.
   */
  protected void unlockDocuments()
  {
      if(lockedDocuments == null)
          {return;}
     
    modifiedDocuments.clear();
     
    //unlock documents
      lockedDocuments.unlock(true);
        lockedDocuments = null;
  }

    public static void checkFragmentation(XQueryContext context, DocumentSet docs) throws EXistException {
        int fragmentationLimit = -1;
        final Object property = context.getBroker().getBrokerPool().getConfiguration().getProperty(DBBroker.PROPERTY_XUPDATE_FRAGMENTATION_FACTOR);
        if (property != null)
            {fragmentationLimit = ((Integer)property).intValue();}
        checkFragmentation(context, docs, fragmentationLimit);
    }

  /**
   * Check if any of the modified documents needs defragmentation.
   *
   * Defragmentation will take place if the number of split pages in the
   * document exceeds the limit defined in the configuration file.
   * 
   * @param docs
   */
  public static void checkFragmentation(XQueryContext context, DocumentSet docs, int splitCount) throws EXistException {
        final DBBroker broker = context.getBroker();
        final TransactionManager txnMgr = context.getBroker().getBrokerPool().getTransactionManager();
       
        //if there is no batch update transaction, start a new individual transaction
        final Txn transaction = txnMgr.beginTransaction();
        try {
            for (final Iterator<DocumentImpl> i = docs.getDocumentIterator(); i.hasNext(); ) {
                final DocumentImpl next = i.next();
                if(next.getMetadata().getSplitCount() > splitCount)
                    {try {
                        next.getUpdateLock().acquire(Lock.WRITE_LOCK);
                        broker.defragXMLResource(transaction, next);
                    } finally {
                        next.getUpdateLock().release(Lock.WRITE_LOCK);
                    }}
                broker.checkXMLResourceConsistency(next);
            }
           
            txnMgr.commit(transaction);
        } catch (final Exception e) {
            txnMgr.abort(transaction);
        } finally {
            txnMgr.close(transaction);
        }
    }
 
  /**
   * Fires the prepare function for the UPDATE_DOCUMENT_EVENT trigger for the Document doc
   * 
   * @param transaction  The transaction
   * @param doc  The document to trigger for
   *
   * @throws TriggerException
   */
  private void prepareTrigger(Txn transaction, DocumentImpl doc) throws TriggerException {

      final Collection col = doc.getCollection();
            final DBBroker broker = context.getBroker();
           
            final DocumentTrigger trigger = new DocumentTriggers(broker, col);
           
            //prepare the trigger
            trigger.beforeUpdateDocument(context.getBroker(), transaction, doc);
            triggers.put(doc.getDocId(), trigger);
  }
 
  /** Fires the finish function for UPDATE_DOCUMENT_EVENT for the documents trigger
   *
   * @param transaction  The transaction
   * @param doc  The document to trigger for
   *
   * @throws TriggerException
   */
  private void finishTrigger(Txn transaction, DocumentImpl doc) throws TriggerException {
            //finish the trigger
            final DocumentTrigger trigger = triggers.get(doc.getDocId());
            if(trigger != null) {
                trigger.afterUpdateDocument(context.getBroker(), transaction, doc);
            }
  }
 
  /**
   * Gets the Transaction to use for the update (can be batch or individual)
   *
   * @return The transaction
   */
  public Txn getTransaction()
  {
            final TransactionManager txnMgr = context.getBroker().getBrokerPool().getTransactionManager();
            final Txn transaction = txnMgr.beginTransaction();

            return transaction;
  }
 
  /**
   * Commit's the transaction for the update unless it is a batch update and then the commit is defered
   *
   * @param transaction The Transaction to commit
   */
  public void commitTransaction(Txn transaction) throws TransactionException
 
            final TransactionManager txnMgr = context.getBroker().getBrokerPool().getTransactionManager();
            txnMgr.commit(transaction);
  }

    public void abortTransaction(Txn transaction) {
        final TransactionManager txnMgr = context.getBroker().getBrokerPool().getTransactionManager();
        txnMgr.abort(transaction);
    }

    public void closeTransaction(Txn transaction) {
        final TransactionManager txnMgr = context.getBroker().getBrokerPool().getTransactionManager();
        txnMgr.close(transaction);
    }

    final static class IndexListener implements NodeIndexListener {

    StoredNode[] nodes;

    public IndexListener(StoredNode[] nodes) {
      this.nodes = nodes;
    }

    /* (non-Javadoc)
     * @see org.exist.dom.NodeIndexListener#nodeChanged(org.exist.dom.NodeImpl)
     */
    public void nodeChanged(StoredNode node) {
      final long address = node.getInternalAddress();
      for (int i = 0; i < nodes.length; i++) {
        if (StorageAddress.equals(nodes[i].getInternalAddress(), address)) {
          nodes[i] = node;
        }
      }
    }
  }
}
TOP

Related Classes of org.exist.xquery.update.Modification

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.