Package org.structr.core.graph

Source Code of org.structr.core.graph.TransactionCommand

/**
* Copyright (C) 2010-2014 Morgner UG (haftungsbeschränkt)
*
* This file is part of Structr <http://structr.org>.
*
* Structr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Structr 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Structr.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.structr.core.graph;


import java.util.LinkedHashSet;
import java.util.Set;
import java.util.logging.Level;
import org.neo4j.graphdb.GraphDatabaseService;

//~--- JDK imports ------------------------------------------------------------

import java.util.logging.Logger;
import org.structr.common.error.ErrorBuffer;
import org.structr.common.error.FrameworkException;
import org.structr.core.StructrTransactionListener;
import org.structr.core.entity.AbstractNode;
import org.structr.core.property.PropertyKey;

//~--- classes ----------------------------------------------------------------

/**
* Graph service command for database operations that need to be wrapped in
* a transaction. All operations that modify the database need to be executed
* in a transaction, which can be achieved using the following code:
*
* <pre>
* StructrApp.getInstance(securityContext).command(TransactionCommand.class).execute(new StructrTransaction() {
*
*  public Object execute() throws FrameworkException {
*    // do stuff here
*  }
* });
* </pre>
*
* @author Christian Morgner
*/
public class TransactionCommand extends NodeServiceCommand implements AutoCloseable {

  private static final Logger logger                                  = Logger.getLogger(TransactionCommand.class.getName());
  private static final Set<StructrTransactionListener> listeners      = new LinkedHashSet<>();
  private static final ThreadLocal<ModificationQueue> queues          = new ThreadLocal<>();
  private static final ThreadLocal<ErrorBuffer> buffers               = new ThreadLocal<>();
  private static final ThreadLocal<TransactionCommand> currentCommand = new ThreadLocal<>();
  private static final ThreadLocal<TransactionReference> transactions = new ThreadLocal<>();
  private static final MultiSemaphore                    semaphore    = new MultiSemaphore();

  public TransactionCommand beginTx() {
   
    final GraphDatabaseService graphDb = (GraphDatabaseService) arguments.get("graphDb");
    TransactionReference tx            = transactions.get();
   
    if (tx == null) {
   
      // start new transaction
      tx = new TransactionReference(graphDb.beginTx());
     
      queues.set(new ModificationQueue());
      buffers.set(new ErrorBuffer());
      transactions.set(tx);
      currentCommand.set(this);
    }
   
    // increase depth
    tx.begin();
   
    return this;
  }
 
  public void commitTx(final boolean doValidation) throws FrameworkException {
 
    final TransactionReference tx = transactions.get();
    if (tx != null && tx.isToplevel()) {

      final ModificationQueue modificationQueue = queues.get();
      final ErrorBuffer errorBuffer             = buffers.get();
     
      // 1. do inner callbacks (may cause transaction to fail)
      if (doValidation && !modificationQueue.doInnerCallbacks(securityContext, errorBuffer)) {

        // create error
        if (doValidation) {

          tx.failure();

          throw new FrameworkException(422, errorBuffer);
        }
      }
     
      // 1.5: execute validatable post-transaction action
      if (doValidation && !modificationQueue.doPostProcessing(securityContext, errorBuffer)) {

        tx.failure();

        throw new FrameworkException(422, errorBuffer);
      }

      // 2. fetch all types of entities modified in this tx
      Set<String> synchronizationKeys = modificationQueue.getSynchronizationKeys();

      // we need to protect the validation and indexing part of every transaction
      // from being entered multiple times in the presence of validators
      // 3. acquire semaphores for each modified type
      try { semaphore.acquire(synchronizationKeys); } catch (InterruptedException iex) { return; }

      // finally, do validation under the protection of the semaphores for each type
      if (!modificationQueue.doValidation(securityContext, errorBuffer, doValidation)) {

        tx.failure();

        // release semaphores as the transaction is now finished
        semaphore.release(synchronizationKeys)// careful: this can be null

        // create error
        throw new FrameworkException(422, errorBuffer);
      }

      try {
        tx.success();

      } catch (Throwable t) {
        t.printStackTrace();
      }

      // release semaphores as the transaction is now finished
      semaphore.release(synchronizationKeys)// careful: this can be null
    }
  }
 
  public ModificationQueue finishTx() {
   
    final TransactionReference tx       = transactions.get();
    ModificationQueue modificationQueue = null;
   
    if (tx != null) {
     
      if (tx.isToplevel()) {

        modificationQueue = queues.get();
       
        // cleanup
        queues.remove();
        buffers.remove();
        currentCommand.remove();
        transactions.remove();

        try {
          tx.close();
         
        } catch (Throwable t) {
          t.printStackTrace();
        }
       
      } else {
       
        tx.end();
      }
    }

    return modificationQueue;
  }
 
  @Override
  public void close() throws FrameworkException {
    finishTx();
  }
 
  public static void postProcess(final String key, final TransactionPostProcess process) {
   
    TransactionCommand command = currentCommand.get();
    if (command != null) {
     
      ModificationQueue modificationQueue = command.getModificationQueue();
      if (modificationQueue != null) {
       
        modificationQueue.postProcess(key, process);
       
      } else {
       
        logger.log(Level.SEVERE, "Got empty changeSet from command!");
      }
     
    } else {
     
      logger.log(Level.SEVERE, "Trying to register transaction post processing while outside of transaction!");
    }
   
  }
 
  public static void nodeCreated(NodeInterface node) {
   
    TransactionCommand command = currentCommand.get();
    if (command != null) {
     
      ModificationQueue modificationQueue = command.getModificationQueue();
      if (modificationQueue != null) {
       
        modificationQueue.create(node);
       
      } else {
       
        logger.log(Level.SEVERE, "Got empty changeSet from command!");
      }
     
    } else {
     
      logger.log(Level.SEVERE, "Node created while outside of transaction!");
    }
  }
 
  public static void nodeModified(AbstractNode node, PropertyKey key, Object previousValue, Object newValue) {
   
    TransactionCommand command = currentCommand.get();
    if (command != null) {
     
      ModificationQueue modificationQueue = command.getModificationQueue();
      if (modificationQueue != null) {
       
        modificationQueue.modify(node, key, previousValue, newValue);
       
      } else {
       
        logger.log(Level.SEVERE, "Got empty changeSet from command!");
      }
     
    } else {
     
      logger.log(Level.SEVERE, "Node deleted while outside of transaction!");
    }
  }
 
  public static void nodeDeleted(NodeInterface node) {
   
    TransactionCommand command = currentCommand.get();
    if (command != null) {
     
      ModificationQueue modificationQueue = command.getModificationQueue();
      if (modificationQueue != null) {
       
        modificationQueue.delete(node);
       
      } else {
       
        logger.log(Level.SEVERE, "Got empty changeSet from command!");
      }
     
    } else {
     
      logger.log(Level.SEVERE, "Node deleted while outside of transaction!");
    }
  }
 
  public static void relationshipCreated(RelationshipInterface relationship) {
   
    TransactionCommand command = currentCommand.get();
    if (command != null) {
     
      ModificationQueue modificationQueue = command.getModificationQueue();
      if (modificationQueue != null) {
       
        modificationQueue.create(relationship);
       
      } else {
       
        logger.log(Level.SEVERE, "Got empty changeSet from command!");
      }
     
    } else {
     
      logger.log(Level.SEVERE, "Relationships created while outside of transaction!");
    }
  }
 
  public static void relationshipModified(RelationshipInterface relationship, PropertyKey key, Object previousValue, Object newValue) {
   
    TransactionCommand command = currentCommand.get();
    if (command != null) {
     
      ModificationQueue modificationQueue = command.getModificationQueue();
      if (modificationQueue != null) {
       
        modificationQueue.modify(relationship, key, previousValue, newValue);
       
      } else {
       
        logger.log(Level.SEVERE, "Got empty changeSet from command!");
      }
     
    } else {
     
      logger.log(Level.SEVERE, "Relationship deleted while outside of transaction!");
    }
  }
 
  public static void relationshipDeleted(RelationshipInterface relationship, boolean passive) {
   
    TransactionCommand command = currentCommand.get();
    if (command != null) {
     
      ModificationQueue modificationQueue = command.getModificationQueue();
      if (modificationQueue != null) {
       
        modificationQueue.delete(relationship, passive);
       
      } else {
       
        logger.log(Level.SEVERE, "Got empty changeSet from command!");
      }
     
    } else {
     
      logger.log(Level.SEVERE, "Relationship deleted while outside of transaction!");
    }
  }
 
  public static void registerTransactionListener(final StructrTransactionListener listener) {
    listeners.add(listener);
  }
 
  public static void removeTransactionListener(final StructrTransactionListener listener) {
    listeners.remove(listener);
  }
 
  public static Set<StructrTransactionListener> getTransactionListeners() {
    return listeners;
  }
 
  public static boolean inTransaction() {
    return currentCommand.get() != null;
  }

  private ModificationQueue getModificationQueue() {
    return queues.get();
  }
}
TOP

Related Classes of org.structr.core.graph.TransactionCommand

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.