package org.qi4j.entitystore.neo4j; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import org.neo4j.graphdb.Node; import org.neo4j.index.IndexService; import org.neo4j.kernel.EmbeddedGraphDatabase; import org.qi4j.api.entity.EntityDescriptor; import org.qi4j.api.entity.EntityReference; import org.qi4j.api.structure.Module; import org.qi4j.api.value.ValueSerialization; import org.qi4j.spi.entity.EntityState; import org.qi4j.spi.entity.EntityStatus; import org.qi4j.spi.entitystore.*; import static org.qi4j.functional.Iterables.first; public class NeoEntityStoreUnitOfWork implements EntityStoreUnitOfWork, StateCommitter { static final String ENTITY_STATE_ID = "entity_state_id"; static final String ENTITY_TYPE = "entity_type"; private final EmbeddedGraphDatabase neo; private final IndexService indexService; private final ValueSerialization valueSerialization; private long currentTime; private final TransactionManager tm; private final Transaction transaction; private final String identity; private final Module module; NeoEntityStoreUnitOfWork( EmbeddedGraphDatabase neo, IndexService indexService, ValueSerialization valueSerialization, String identity, Module module, long currentTime ) { this.neo = neo; this.indexService = indexService; this.valueSerialization = valueSerialization; this.currentTime = currentTime; this.tm = this.neo.getConfig().getTxModule().getTxManager(); this.transaction = beginTransaction(); this.identity = identity; this.module = module; } @Override public StateCommitter applyChanges() throws EntityStoreException { return this; } @Override public long currentTime() { return currentTime; } @Override public void discard() { cancel(); } Node getEntityStateNode( EntityReference anIdentity ) { Node node = indexService.getSingleNode( ENTITY_STATE_ID, anIdentity.identity() ); if( node == null ) { throw new EntityNotFoundException( anIdentity ); } return node; } @Override public EntityState entityStateOf( EntityReference anIdentity ) throws EntityStoreException, EntityNotFoundException { return new NeoEntityState( valueSerialization, this, getEntityStateNode( anIdentity ), EntityStatus.LOADED ); } @Override public EntityState newEntityState( EntityReference anIdentity, EntityDescriptor entityDescriptor ) throws EntityStoreException { String type = first(entityDescriptor.types()).getName(); Node typeNode = indexService.getSingleNode( ENTITY_TYPE, type ); if( typeNode == null ) { typeNode = createEntityType( type ); } Node node = indexService.getSingleNode( ENTITY_STATE_ID, anIdentity.identity() ); if( node != null ) { throw new EntityAlreadyExistsException( anIdentity ); } node = neo.createNode(); node.setProperty( NeoEntityState.VERSION, 0l ); node.setProperty( NeoEntityState.MODIFIED, currentTime ); node.createRelationshipTo( typeNode, RelTypes.IS_OF_TYPE ); node.setProperty( NeoEntityState.ENTITY_ID, anIdentity.identity() ); indexService.index( node, ENTITY_STATE_ID, anIdentity.identity() ); return new NeoEntityState( valueSerialization, this, node, EntityStatus.NEW ); } @Override public void cancel() { try { tm.rollback(); } catch( Exception e ) { throw new EntityStoreException( "Failed to rollback transaction.", e ); } } @Override public void commit() { try { tm.commit(); } catch( Exception e ) { throw new EntityStoreException( "Failed to commit transaction.", e ); } } private Transaction beginTransaction() { try { tm.begin(); return tm.getTransaction(); } catch( Exception e ) { throw new EntityStoreException( "Failed to begin transaction.", e ); } } void suspend() { try { Transaction txRunning = tm.getTransaction(); if( txRunning != null && txRunning == transaction ) { tm.suspend(); } } catch( Exception e ) { throw new EntityStoreException( "Failed to suspend " + transaction, e ); } } void resume() { try { tm.resume( transaction ); } catch( Exception e ) { throw new EntityStoreException( "Failed to resume " + transaction, e ); } } EmbeddedGraphDatabase getNeo() { return neo; } IndexService getIndexService() { return indexService; } @Override public String identity() { return identity; } private Node createEntityType( String type ) { Node typeNode = neo.createNode(); neo.getReferenceNode().createRelationshipTo( typeNode, RelTypes.ENTITY_TYPE_REF ); typeNode.setProperty( ENTITY_TYPE, type ); indexService.index( typeNode, ENTITY_TYPE, type ); return typeNode; } EntityDescriptor getEntityDescriptor( String type ) { return ( (Module) module ).entityDescriptor( type ); } Module getModule() { return module; } }