/**
* Copyright (c) 2002-2013 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j 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.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.android.service;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.neo4j.android.common.IGraphDatabase;
import org.neo4j.android.common.INodeIterator;
import org.neo4j.android.common.IRelationshipIterator;
import org.neo4j.android.common.ParcelableError;
import org.neo4j.android.common.ParcelableIndexValue;
import org.neo4j.android.common.ParcelableNode;
import org.neo4j.android.common.ParcelableRelationship;
import org.neo4j.android.common.ParcelableTraversalDescription;
import org.neo4j.android.service.util.ParcelableFactory;
import org.neo4j.android.service.util.SimpleRelationshipType;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.graphdb.index.RelationshipIndex;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.javax.transaction.InvalidTransactionException;
import org.neo4j.javax.transaction.SystemException;
import org.neo4j.javax.transaction.TransactionManager;
import org.neo4j.kernel.EmbeddedGraphDatabase;
import org.neo4j.kernel.TopLevelTransaction;
import org.neo4j.kernel.Traversal;
import org.neo4j.kernel.Uniqueness;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.util.Log;
/**
* An IPC-wrapper around a Neo4j graph database.
*/
public class DbWrapper extends IGraphDatabase.Stub {
private static final String TAG = DbWrapper.class.getSimpleName();
/**
* Our transaction Binder-to-Transaction mapper
*/
private TrxManager mTrxManager;
/**
* The graph database we are exposing via IPC
*/
private EmbeddedGraphDatabase mDb;
/**
* The service context, required for permission checks.
*/
private Context mContext;
public DbWrapper(EmbeddedGraphDatabase db, TrxManager mgr, Context context) {
mDb = db;
mTrxManager = mgr;
mContext = context;
}
@Override
public INodeIterator getAllNodes(ParcelableError err) throws RemoteException {
try {
resumeTrxIfExists();
try {
return new NodeIteratorWrapper(mDb.getAllNodes().iterator());
} finally {
suspendCurrentTrx("getAllNodes");
}
} catch (Exception e) {
Log.e(TAG, "Error accessing nodes", e);
err.setError(Errors.TRANSACTION, e.getMessage());
return null;
}
}
@Override
public List<String> getRelationshipTypes(ParcelableError err) throws RemoteException {
try {
resumeTrxIfExists();
try {
ArrayList<String> names = new ArrayList<String>();
for (RelationshipType type : mDb.getRelationshipTypes()) {
names.add(type.name());
}
return names;
} finally {
suspendCurrentTrx("getRelationshipTypes");
}
} catch (Exception e) {
Log.e(TAG, "Error accessing relationship types", e);
err.setError(Errors.TRANSACTION, e.getMessage());
return null;
}
}
@Override
public ParcelableNode getReferenceNode(ParcelableError err) throws RemoteException {
try {
resumeTrxIfExists();
try {
Node refNode = mDb.getReferenceNode();
return ParcelableFactory.makeParcelableNode(refNode);
} finally {
suspendCurrentTrx("getReferenceNode");
}
} catch (Exception e) {
Log.e(TAG, "Error accessing reference node node", e);
err.setError(Errors.TRANSACTION, e.getMessage());
return null;
}
}
@Override
public ParcelableNode getNodeById(long id, ParcelableError err) throws RemoteException {
try {
resumeTrxIfExists();
try {
Node node = mDb.getNodeById(id);
return ParcelableFactory.makeParcelableNode(node);
} finally {
suspendCurrentTrx("getNodeById");
}
} catch (Exception e) {
Log.e(TAG, "Error accessing node", e);
err.setError(Errors.TRANSACTION, e.getMessage());
return null;
}
}
@Override
public ParcelableRelationship getRelationshipById(long id, ParcelableError err) throws RemoteException {
try {
resumeTrxIfExists();
try {
Relationship rel = mDb.getRelationshipById(id);
return ParcelableFactory.makeParcelableRelationship(rel);
} finally {
suspendCurrentTrx("getRelationshipById");
}
} catch (Exception e) {
Log.e(TAG, "Error accessing relationship", e);
err.setError(Errors.TRANSACTION, e.getMessage());
return null;
}
}
@Override
public long createNode(ParcelableNode node, ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Node newNode = mDb.createNode();
for (String key : node.getPropertyKeys()) {
newNode.setProperty(key, node.getProperty(key));
}
return newNode.getId();
} finally {
suspendCurrentTrx("createNode");
}
} catch (Exception e) {
Log.e(TAG, "Error creating node", e);
err.setError(Errors.TRANSACTION, e.getMessage());
return -1; // we need to return something, caller checks error
// object
}
}
@Override
public void deleteNode(long id, ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Node node = mDb.getNodeById(id); // will throw NotFoundException
// if node does not exist
node.delete();
} finally {
suspendCurrentTrx("deleteNode");
}
} catch (Exception e) {
Log.e(TAG, "Error deleting node", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void updateNode(ParcelableNode node, ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Node target = mDb.getNodeById(node.getId());
if (target != null) {
// remove existing properties
for (String key : target.getPropertyKeys()) {
target.removeProperty(key);
}
// set new properties
for (String key : node.getPropertyKeys()) {
target.setProperty(key, node.getProperty(key));
}
} else {
String message = "node not found. node '" + node + "'";
Log.e(TAG, message);
err.setError(Errors.TRANSACTION, message);
}
} finally {
suspendCurrentTrx("updateNode");
}
} catch (Exception e) {
Log.e(TAG, "Failed to update node", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public long createRelationship(ParcelableRelationship rel, ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Node startNode = mDb.getNodeById(rel.getStartNodeId());
Node endNode = mDb.getNodeById(rel.getEndNodeId());
Relationship newRel = startNode.createRelationshipTo(endNode, new SimpleRelationshipType(rel.getName()));
for (String key : rel.getPropertyKeys()) {
newRel.setProperty(key, rel.getProperty(key));
}
return newRel.getId();
} finally {
suspendCurrentTrx("createRelationship");
}
} catch (Exception e) {
Log.e(TAG, "Error creating relationship", e);
err.setError(Errors.TRANSACTION, e.getMessage());
return -1; // we need to return something, caller checks error
// object
}
}
@Override
public void updateRelationship(ParcelableRelationship rel, ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Relationship target = mDb.getRelationshipById(rel.getId());
// remove existing properties
for (String key : target.getPropertyKeys()) {
target.removeProperty(key);
}
// set new properties
for (String key : rel.getPropertyKeys()) {
target.setProperty(key, rel.getProperty(key));
}
} finally {
suspendCurrentTrx("updateRelationship");
}
} catch (Exception e) {
Log.e(TAG, "Error updating relationship", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void deleteRelationship(long id, ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Relationship rel = mDb.getRelationshipById(id);
rel.delete();
} finally {
suspendCurrentTrx("deleteRelationship");
}
} catch (Exception e) {
Log.e(TAG, "Error deleting relationship", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public INodeIterator traverse(ParcelableTraversalDescription desc, long startNodeId, ParcelableError err)
throws RemoteException {
try {
resumeTrxIfExists();
try {
Node startNode = mDb.getNodeById(startNodeId);
if (startNode == null) {
throw new IllegalArgumentException("Illegal start node for traversal");
}
TraversalDescription traversalDesc = Traversal.description();
// map order
switch (desc.getOrder()) {
case BREADTH_FIRST:
traversalDesc = traversalDesc.breadthFirst();
break;
case DEPTH_FIRST:
traversalDesc = traversalDesc.depthFirst();
break;
}
// map uniqueness
switch (desc.getUniqueness()) {
case NONE:
traversalDesc = traversalDesc.uniqueness(Uniqueness.NONE);
break;
case NODE_GLOBAL:
traversalDesc = traversalDesc.uniqueness(Uniqueness.NODE_GLOBAL);
break;
case NODE_RECENT:
traversalDesc = traversalDesc.uniqueness(Uniqueness.NODE_RECENT);
break;
case NODE_PATH:
traversalDesc = traversalDesc.uniqueness(Uniqueness.NODE_PATH);
break;
case RELATIONSHIP_GLOBAL:
traversalDesc = traversalDesc.uniqueness(Uniqueness.RELATIONSHIP_GLOBAL);
break;
case RELATIONSHIP_PATH:
traversalDesc = traversalDesc.uniqueness(Uniqueness.RELATIONSHIP_PATH);
break;
case RELATIONSHIP_RECENT:
traversalDesc = traversalDesc.uniqueness(Uniqueness.RELATIONSHIP_RECENT);
break;
}
// map relationships
for (String name : desc.getRelationships().keySet()) {
RelationshipType type = new SimpleRelationshipType(name);
Direction direction = Direction.valueOf(desc.getRelationships().get(name).name());
traversalDesc = traversalDesc.relationships(type, direction);
}
// TODO: support paths
Iterator<Node> nodeIterator = traversalDesc.traverse(startNode).nodes().iterator();
return new NodeIteratorWrapper(nodeIterator);
} finally {
suspendCurrentTrx("traverse");
}
} catch (Exception e) {
err.setError(Errors.TRANSACTION, e.getMessage());
return null;
}
}
// -------------------------------------------------------------------------
// Transactions
// -------------------------------------------------------------------------
@Override
public void beginTx(ParcelableError err) throws RemoteException {
Log.i(TAG, "BEGIN TRANSACTION '" + this.asBinder().hashCode() + "'");
try {
// cannot start transactions without write permission
checkCallerHasWritePermission();
if (mTrxManager.hasAssociatedTrx(this.asBinder())) {
throw new IllegalStateException("A transaction is already associated with this Binder");
}
// acquire and associate transaction
try {
Transaction trx = mDb.beginTx();
TransactionManager trxMgr = mDb.getConfig().getTxModule().getTxManager();
mTrxManager.associateTrx(this.asBinder(), trx, trxMgr);
} finally {
suspendCurrentTrx("beginTx");
}
} catch (Exception e) {
Log.e(TAG, "Failed to mark trx as failure", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void txFailure(ParcelableError err) throws RemoteException {
try {
Transaction trx = resumeTrx();
try {
trx.failure();
} finally {
suspendCurrentTrx("txFailure");
Log.i(TAG, "END TRANSACTION - FAIL '" + this.asBinder().hashCode() + "'");
}
} catch (Exception e) {
Log.e(TAG, "Failed to mark trx as failure, will disassociate trx", e);
mTrxManager.disassociateTrx(this.asBinder(), "txFailure");
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void txSuccess(ParcelableError err) throws RemoteException {
try {
Transaction trx = resumeTrx();
try {
trx.success();
} finally {
suspendCurrentTrx("txSuccess");
Log.i(TAG, "END TRANSACTION - SUCCESS '" + this.asBinder().hashCode() + "'");
}
} catch (Exception e) {
Log.e(TAG, "Failed to mark trx as success, will disassociate trx", e);
mTrxManager.disassociateTrx(this.asBinder(), "txSuccess");
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void txFinish(ParcelableError err) throws RemoteException {
try {
Transaction trx = resumeTrx();
trx.finish();
} catch (Exception e) {
Log.e(TAG, "Failed to mark trx as finished, will disassociate trx", e);
err.setError(Errors.TRANSACTION, e.getMessage());
} finally {
mTrxManager.disassociateTrx(this.asBinder(), "txFinish");
Log.i(TAG, "END TRANSACTION - FINISH '" + this.asBinder().hashCode() + "'");
}
}
// -------------------------------------------------------------------------
// Node Indexing support
// -------------------------------------------------------------------------
@Override
public void createNodeIndex(String name, ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
IndexManager index = mDb.index();
index.forNodes(name); // this will create the index
} finally {
suspendCurrentTrx("createNodeIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to create/access node index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public boolean nodeIndexExists(String name, ParcelableError err) throws RemoteException {
try {
resumeTrxIfExists();
try {
IndexManager index = mDb.index();
return index.existsForNodes(name);
} finally {
suspendCurrentTrx("nodeIndexExists");
}
} catch (Exception e) {
Log.e(TAG, "Failed to determine if node index '" + name + "' exists", e);
err.setError(Errors.TRANSACTION, e.getMessage());
return false; // dummy return value
}
}
@Override
public void deleteNodeIndex(String name, ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
IndexManager index = mDb.index();
index.forNodes(name); // this will create the index
} finally {
suspendCurrentTrx("deleteNodeIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to delete node index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void addNodeToIndex(String name, long nodeId, String key, ParcelableIndexValue value, ParcelableError err)
throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Index<Node> nodeIndex = mDb.index().forNodes(name);
Node node = mDb.getNodeById(nodeId); // will throw
// NotFoundException if id
// is invalid
nodeIndex.add(node, key, value.get());
} finally {
suspendCurrentTrx("addNodeToIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to add node to index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void updateNodeInIndex(String name, long nodeId, String key, ParcelableIndexValue value, ParcelableError err)
throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Index<Node> nodeIndex = mDb.index().forNodes(name);
Node node = mDb.getNodeById(nodeId); // will throw
// NotFoundException if id
// is invalid
// See http://docs.neo4j.org/chunked/stable/indexing-update.html
nodeIndex.remove(node, key, value.get());
nodeIndex.add(node, key, value.get());
} finally {
suspendCurrentTrx("updateNodeInIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to update node in index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void removeNodeFromIndex(String name, long nodeId, ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Index<Node> nodeIndex = mDb.index().forNodes(name);
Node node = mDb.getNodeById(nodeId); // will throw
// NotFoundException if id
// is invalid
nodeIndex.remove(node);
} finally {
suspendCurrentTrx("removeNodeFromIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to remove node from index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void removeNodeKeyFromIndex(String name, long nodeId, String key, ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Index<Node> nodeIndex = mDb.index().forNodes(name);
Node node = mDb.getNodeById(nodeId); // will throw
// NotFoundException if id
// is invalid
nodeIndex.remove(node, key);
} finally {
suspendCurrentTrx("removeNodeKeyFromIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to remove node from index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void removeNodeKeyValueFromIndex(String name, long nodeId, String key, ParcelableIndexValue value, ParcelableError err)
throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Index<Node> nodeIndex = mDb.index().forNodes(name);
Node node = mDb.getNodeById(nodeId); // will throw
// NotFoundException if id
// is invalid
nodeIndex.remove(node, key, value.get());
} finally {
suspendCurrentTrx("removeNodeKeyValueFromIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to remove node from index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public INodeIterator getNodesFromIndex(String name, String key, ParcelableIndexValue value, ParcelableError err)
throws RemoteException {
try {
resumeTrxIfExists();
try {
Index<Node> nodeIndex = mDb.index().forNodes(name);
IndexHits<Node> hits = nodeIndex.get(key, value.get());
return new NodeIteratorWrapper(hits.iterator());
} finally {
suspendCurrentTrx("getNodesFromIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to remove node from index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
return null;
}
}
// -------------------------------------------------------------------------
// Relationship Indexing support
// -------------------------------------------------------------------------
@Override
public void createRelationshipIndex(String name, ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
mDb.index().forRelationships(name); // this will create the
// index
} finally {
suspendCurrentTrx("createRelationshipIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to create/access relationship index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public boolean relationshipIndexExists(String name, ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
boolean indexExists = mDb.index().existsForRelationships(name);
return indexExists;
} finally {
suspendCurrentTrx("relationshipIndexExists");
}
} catch (Exception e) {
Log.e(TAG, "Failed to access relationship index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
return false; // have to return something
}
}
@Override
public void deleteRelationshipIndex(String name, ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
RelationshipIndex index = mDb.index().forRelationships(name); // this
// will
// create
// the
// index
index.delete();
} finally {
suspendCurrentTrx("deleteRelationshipIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to delete relationship index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void addRelationshipToIndex(String name, long relationshipId, String key, ParcelableIndexValue value, ParcelableError err)
throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Relationship rel = mDb.getRelationshipById(relationshipId);
RelationshipIndex index = mDb.index().forRelationships(name); // this
// will
// create
// the
// index
index.add(rel, key, value.get());
} finally {
suspendCurrentTrx("addRelationshipToIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to add relationship to index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void updateRelationshipInIndex(String name, long relationshipId, String key, ParcelableIndexValue value,
ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Relationship rel = mDb.getRelationshipById(relationshipId);
RelationshipIndex index = mDb.index().forRelationships(name); // this
// will
// create
// the
// index
// See http://docs.neo4j.org/chunked/stable/indexing-update.html
index.remove(rel, key, value.get());
index.add(rel, key, value.get());
} finally {
suspendCurrentTrx("updateRelationshipInIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to update relationship in index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void removeRelationshipFromIndex(String name, long relationshipId, ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Relationship rel = mDb.getRelationshipById(relationshipId);
RelationshipIndex index = mDb.index().forRelationships(name); // this
// will
// create
// the
// index
index.remove(rel);
} finally {
suspendCurrentTrx("removeRelationshipFromIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to add relationship to index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void removeRelationshipKeyFromIndex(String name, long relationshipId, String key, ParcelableError err)
throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Relationship rel = mDb.getRelationshipById(relationshipId);
RelationshipIndex index = mDb.index().forRelationships(name); // this
// will
// create
// the
// index
index.remove(rel, key);
} finally {
suspendCurrentTrx("removeRelationshipKeyFromIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to add relationship to index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public void removeRelationshipKeyValueFromIndex(String name, long relationshipId, String key, ParcelableIndexValue value,
ParcelableError err) throws RemoteException {
try {
checkCallerHasWritePermission();
resumeTrx();
try {
Relationship rel = mDb.getRelationshipById(relationshipId);
RelationshipIndex index = mDb.index().forRelationships(name); // this
// will
// create
// the
// index
index.remove(rel, key, value.get());
} finally {
suspendCurrentTrx("removeRelationshipKeyValueFromIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to add relationship to index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
}
}
@Override
public IRelationshipIterator getRelationshipsFromIndex(String name, String key, ParcelableIndexValue value, ParcelableError err)
throws RemoteException {
try {
resumeTrxIfExists();
try {
RelationshipIndex index = mDb.index().forRelationships(name); // this
// will
// create
// the
// index
IndexHits<Relationship> hits = index.get(key, value.get());
return new RelationshipIteratorWrapper(hits);
} finally {
suspendCurrentTrx("getRelationshipsFromIndex");
}
} catch (Exception e) {
Log.e(TAG, "Failed to add relationship to index '" + name + "'", e);
err.setError(Errors.TRANSACTION, e.getMessage());
return null;
}
}
// -------------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------------
private void assertTransactionExists() {
boolean hasTransaction = mTrxManager.hasAssociatedTrx(this.asBinder());
// log.d("hasTransaction: " + hasTransaction);
if (!hasTransaction) {
throw new IllegalStateException("No transaction associated with this Binder");
}
}
/**
* Resume the transaction that is associated with this Binder.
*
* @param required if a transaction is required
* @return the transaction
*/
private Transaction resumeTrx() throws InvalidTransactionException, SystemException {
assertTransactionExists();
Transaction trx = mTrxManager.getAssociatedTrx(this.asBinder());
jtaResume(trx);
return trx;
}
/**
* Resume a transaction, if one is associated with this Binder instance.
*
* @return the transaction object, or null if no transaction was associated
* with the binder.
* @throws InvalidTransactionException
* @throws SystemException
*/
private Transaction resumeTrxIfExists() throws InvalidTransactionException, SystemException {
Transaction trx = mTrxManager.getAssociatedTrx(this.asBinder());
if (trx != null) {
jtaResume(trx);
}
return trx;
}
/**
* Resume the JTA transaction.
*
* @param trx
* @throws SystemException
* @throws InvalidTransactionException
* @throws IllegalStateException
*/
private void jtaResume(Transaction trx) throws SystemException, InvalidTransactionException, IllegalStateException {
if (trx == null) {
throw new IllegalArgumentException("Null transaction passed");
}
org.neo4j.javax.transaction.Transaction jtaTrx = ((TopLevelTransaction) trx).getJtaTransaction();
TransactionManager trxManager = mDb.getConfig().getTxModule().getTxManager();
trxManager.resume(jtaTrx); // this will throw an exception if an
// exception is already associated with this
// thread
}
/**
* Suspend this thread's current transaction, if one exists.
*
* @throws SystemException
*/
private void suspendCurrentTrx(String operationName) throws SystemException {
mTrxManager.suspendCurrentTrx(this.asBinder(), operationName);
TransactionManager trxManager = mDb.getConfig().getTxModule().getTxManager();
if (trxManager.getTransaction() != null) {
// log.d("Found running transaction associated with this thread, suspending it");
trxManager.suspend();
}
}
/**
* Check if the caller has permission to write data.
*
* @throws SecurityException if the caller does not have the permission to
* write data
*/
private void checkCallerHasWritePermission() throws SecurityException {
int access = mContext.checkCallingPermission(Permissions.PERM_WRITE);
if (access == PackageManager.PERMISSION_GRANTED) {
return;
}
Log.w(TAG, "IPC caller lacks required permission: " + Permissions.PERM_WRITE);
throw new SecurityException("IPC caller lacks required permission: " + Permissions.PERM_WRITE);
}
}