/*
* Copyright (c) 2008-2009 "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 Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.remote;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import org.neo4j.graphdb.Direction;
import org.neo4j.remote.RemoteResponse.ResponseBuilder;
final class BasicServerTransaction
{
private final BasicGraphDatabaseServer server;
private final Transaction transaction;
private final BasicConnection connection;
final Integer id;
private int nodTokenPool = 0;
private int relTokenPool = 0;
private int strTokenPool = 0;
private final Map<Integer, SimpleIterator<RelationshipSpecification>> relIter = new HashMap<Integer, SimpleIterator<RelationshipSpecification>>();
private final Map<Integer, SimpleIterator<NodeSpecification>> nodIter = new HashMap<Integer, SimpleIterator<NodeSpecification>>();
private final Map<Integer, SimpleIterator<String>> strIter = new HashMap<Integer, SimpleIterator<String>>();
BasicServerTransaction( BasicGraphDatabaseServer server, BasicConnection connection,
Transaction transaction )
{
this.server = server;
this.connection = connection;
this.transaction = transaction;
id = connection.allocateTransactionId();
}
// Transaction management
private void resume()
{
server.resumeTransaction( transaction );
}
void commit()
{
resume();
try
{
transaction.commit();
}
catch ( IllegalStateException ex )
{
}
catch ( SecurityException ex )
{
}
catch ( RollbackException ex )
{
}
catch ( HeuristicMixedException ex )
{
}
catch ( HeuristicRollbackException ex )
{
}
catch ( SystemException ex )
{
}
}
void rollback()
{
resume();
try
{
transaction.rollback();
}
catch ( IllegalStateException ex )
{
}
catch ( SystemException e )
{
}
}
// Internal
private ResponseBuilder response()
{
ResponseBuilder builder = connection.response();
server.buildResponse( connection.neo, id, builder );
return builder;
}
private SimpleIterator<NodeSpecification> getNodes( int token )
{
return nodIter.remove( token );
}
private RemoteResponse<IterableSpecification<NodeSpecification>> nodes(
int token, long size, SimpleIterator<NodeSpecification> iterator )
{
NodeSpecification[] result = consume( token, nodIter, iterator,
server.nodesBatchSize( iterator.count() ) ).toArray(
new NodeSpecification[ 0 ] );
if ( iterator.hasNext() )
{
return response().buildPartialNodeResponse( token, size, result );
}
else
{
return response().buildFinalNodeResponse( size, result );
}
}
private SimpleIterator<RelationshipSpecification> getRelationships(
int token )
{
return relIter.remove( token );
}
private RemoteResponse<IterableSpecification<RelationshipSpecification>> relationships(
int token, SimpleIterator<RelationshipSpecification> iterator )
{
RelationshipSpecification[] result = consume( token, relIter, iterator,
server.relationshipsBatchSize( iterator.count() ) ).toArray(
new RelationshipSpecification[ 0 ] );
if ( iterator.hasNext() )
{
return response().buildPartialRelationshipResponse( token, result );
}
else
{
return response().buildFinalRelationshipResponse( result );
}
}
private SimpleIterator<String> getStrings( int token )
{
return strIter.remove( token );
}
private RemoteResponse<IterableSpecification<String>> strings( int token,
SimpleIterator<String> iterator, int batchSize )
{
String[] result = consume( token, strIter, iterator, batchSize )
.toArray( new String[ 0 ] );
if ( iterator.hasNext() )
{
return response().buildPartialStringResponse( token, result );
}
else
{
return response().buildFinalStringResponse( result );
}
}
private RemoteResponse<IterableSpecification<String>> types( int token,
SimpleIterator<String> iterator )
{
return strings( token, iterator, server.typesBatchSize( iterator
.count() ) );
}
private RemoteResponse<IterableSpecification<String>> keys( int token,
SimpleIterator<String> iterator )
{
return strings( token, iterator, server
.keysBatchSize( iterator.count() ) );
}
private static <E> List<E> consume( int token,
Map<Integer, SimpleIterator<E>> map, SimpleIterator<E> iterator,
int batchSize )
{
List<E> result = new ArrayList<E>( batchSize );
int size = 0;
for ( ; iterator.hasNext() && size < batchSize; size++ )
{
result.add( iterator.next() );
}
if ( iterator.hasNext() )
{
map.put( token, iterator );
}
return result;
}
// Communication
RemoteResponse<NodeSpecification> createNode()
{
resume();
@SuppressWarnings( "hiding" )
long id;
try
{
id = server.createNode( connection.neo );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildNodeResponse( id );
}
RemoteResponse<Boolean> hasNodeWithId( long nodeId )
{
resume();
boolean value;
try
{
value = server.hasNodeWithId( connection.neo, nodeId );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildBooleanResponse( value );
}
RemoteResponse<RelationshipSpecification> createRelationship(
String relationshipTypeName, long startNodeId, long endNodeId )
{
resume();
@SuppressWarnings( "hiding" )
long id;
try
{
id = server.createRelationship( connection.neo,
relationshipTypeName, startNodeId, endNodeId );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildRelationshipResponse( id, relationshipTypeName,
startNodeId, endNodeId );
}
RemoteResponse<RelationshipSpecification> getRelationshipById(
long relationshipId )
{
resume();
RelationshipSpecification spec;
try
{
spec = server.getRelationshipById( connection.neo, relationshipId );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildRelationshipResponse( spec.relationshipId,
spec.name, spec.startNodeId, spec.endNodeId );
}
RemoteResponse<Void> deleteNode( long nodeId )
{
resume();
try
{
server.deleteNode( connection.neo, nodeId );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildVoidResponse();
}
RemoteResponse<IterableSpecification<NodeSpecification>> getAllNodes()
{
resume();
SimpleIterator<NodeSpecification> iterator;
long size;
try
{
size = server.getTotalNumberOfNodes( connection.neo );
iterator = server.getAllNodes( connection.neo );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return nodes( nodTokenPool++, size, iterator );
}
RemoteResponse<Void> deleteRelationship( long relationshipId )
{
resume();
try
{
server.deleteRelationship( connection.neo, relationshipId );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildVoidResponse();
}
RemoteResponse<IterableSpecification<RelationshipSpecification>> getAllRelationships(
long nodeId, Direction direction )
{
resume();
SimpleIterator<RelationshipSpecification> iterator;
try
{
iterator = server.getAllRelationships( connection.neo, nodeId,
direction );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return relationships( relTokenPool++, iterator );
}
RemoteResponse<IterableSpecification<RelationshipSpecification>> getMoreRelationships(
int requestToken )
{
resume();
SimpleIterator<RelationshipSpecification> relationships;
try
{
relationships = getRelationships( requestToken );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return relationships( requestToken, relationships );
}
RemoteResponse<Void> closeRelationshipIterator( int requestToken )
{
try
{
getRelationships( requestToken );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildVoidResponse();
}
RemoteResponse<IterableSpecification<NodeSpecification>> getMoreNodes(
int requestToken )
{
resume();
SimpleIterator<NodeSpecification> nodes;
try
{
nodes = getNodes( requestToken );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return nodes( requestToken, -1, nodes );
}
RemoteResponse<Void> closeNodeIterator( int requestToken )
{
try
{
getNodes( requestToken );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildVoidResponse();
}
RemoteResponse<IterableSpecification<String>> getMorePropertyKeys(
int requestToken )
{
resume();
SimpleIterator<String> strings;
try
{
strings = getStrings( requestToken );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return keys( requestToken, strings );
}
RemoteResponse<Void> closePropertyKeyIterator( int requestToken )
{
try
{
getStrings( requestToken );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildVoidResponse();
}
RemoteResponse<IterableSpecification<String>> getMoreRelationshipTypes(
int requestToken )
{
resume();
SimpleIterator<String> strings;
try
{
strings = getStrings( requestToken );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return types( requestToken, strings );
}
RemoteResponse<Void> closeRelationshipTypeIterator( int requestToken )
{
try
{
getStrings( requestToken );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildVoidResponse();
}
RemoteResponse<Object> getNodeProperty( long nodeId, String key )
{
resume();
Object value;
try
{
value = server.getNodeProperty( connection.neo, nodeId, key );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildPropertyResponse( value );
}
RemoteResponse<IterableSpecification<String>> getNodePropertyKeys(
long nodeId )
{
resume();
SimpleIterator<String> strings;
try
{
strings = server.getNodePropertyKeys( connection.neo, nodeId );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return keys( strTokenPool++, strings );
}
RemoteResponse<NodeSpecification> getReferenceNode()
{
resume();
@SuppressWarnings( "hiding" )
long id;
try
{
id = server.getReferenceNode( connection.neo );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildNodeResponse( id );
}
RemoteResponse<Object> getRelationshipProperty( long relationshipId,
String key )
{
resume();
Object value;
try
{
value = server.getRelationshipProperty( connection.neo,
relationshipId, key );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildPropertyResponse( value );
}
RemoteResponse<IterableSpecification<String>> getRelationshipPropertyKeys(
long relationshipId )
{
resume();
SimpleIterator<String> strings;
try
{
strings = server.getRelationshipPropertyKeys( connection.neo,
relationshipId );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return keys( strTokenPool++, strings );
}
RemoteResponse<IterableSpecification<String>> getRelationshipTypes()
{
resume();
SimpleIterator<String> strings;
try
{
strings = server.getRelationshipTypes( connection.neo );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return types( strTokenPool++, strings );
}
RemoteResponse<IterableSpecification<RelationshipSpecification>> getRelationships(
long nodeId, Direction direction, String[] relationshipTypeNames )
{
resume();
SimpleIterator<RelationshipSpecification> relationships;
try
{
relationships = server.getRelationships( connection.neo, nodeId,
direction, relationshipTypeNames );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return relationships( relTokenPool++, relationships );
}
RemoteResponse<Boolean> hasNodeProperty( long nodeId, String key )
{
resume();
boolean value;
try
{
value = server.hasNodeProperty( connection.neo, nodeId, key );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildBooleanResponse( value );
}
RemoteResponse<Boolean> hasRelationshipProperty( long relationshiId,
String key )
{
resume();
boolean value;
try
{
value = server.hasRelationshipProperty( connection.neo,
relationshiId, key );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildBooleanResponse( value );
}
RemoteResponse<Object> removeNodeProperty( long nodeId, String key )
{
resume();
Object value;
try
{
value = server.removeNodeProperty( connection.neo, nodeId, key );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildPropertyResponse( value );
}
RemoteResponse<Object> removeRelationshipProperty( long relationshipId,
String key )
{
resume();
Object value;
try
{
value = server.removeRelationshipProperty( connection.neo,
relationshipId, key );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildPropertyResponse( value );
}
RemoteResponse<Object> setNodeProperty( long nodeId, String key,
Object value )
{
resume();
Object old;
try
{
old = server.setNodeProperty( connection.neo, nodeId, key, value );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildPropertyResponse( old );
}
RemoteResponse<Object> setRelationshipProperty( long relationshipId,
String key, Object value )
{
resume();
Object old;
try
{
old = server.setRelationshipProperty( connection.neo,
relationshipId, key, value );
}
catch ( RuntimeException ex )
{
return response().buildErrorResponse( ex );
}
return response().buildPropertyResponse( old );
}
// indexing
RemoteResponse<IterableSpecification<NodeSpecification>> getIndexNodes(
int indexId, String key, Object value )
{
resume();
SimpleIterator<NodeSpecification> nodes;
try
{
nodes = server.getIndexNodes( connection.neo, indexId, key, value );
}
catch ( Exception ex )
{
return response().buildErrorResponse( ex );
}
return nodes( nodTokenPool++, nodes.size(), nodes );
}
RemoteResponse<Void> indexNode( int indexId, long nodeId, String key,
Object value )
{
resume();
try
{
server.indexNode( connection.neo, indexId, nodeId, key, value );
}
catch ( Exception ex )
{
return response().buildErrorResponse( ex );
}
return response().buildVoidResponse();
}
RemoteResponse<Void> removeIndexNode( int indexId, long nodeId, String key,
Object value )
{
resume();
try
{
server
.removeIndexNode( connection.neo, indexId, nodeId, key, value );
}
catch ( Exception ex )
{
return response().buildErrorResponse( ex );
}
return response().buildVoidResponse();
}
}