/**
 * 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.kernel.impl.core;

import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Lock;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;
import org.neo4j.test.OtherThreadExecutor;
import org.neo4j.test.OtherThreadExecutor.WorkerCommand;
import org.neo4j.test.TestGraphDatabaseFactory;

import static java.util.concurrent.TimeUnit.SECONDS;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.fail;

/**
 * Confirms that a nested {@link Transaction} can grab locks with its
 * explicit methods: {@link Transaction#acquireReadLock(org.neo4j.graphdb.PropertyContainer) acquireReadLock}
 * and {@link Transaction#acquireWriteLock(org.neo4j.graphdb.PropertyContainer) acquireWriteLock}.
 */
public class NestedTransactionLocksIT
{
    private GraphDatabaseService db;
    
    @Before
    public void before() throws Exception
    {
        db = new TestGraphDatabaseFactory().newImpermanentDatabase();
    }

    @After
    public void after() throws Exception
    {
        db.shutdown();
    }

    private WorkerCommand<Void, Lock> acquireWriteLock( final Node resource )
    {
        return new WorkerCommand<Void, Lock>()
        {
            @Override
            public Lock doWork( Void state )
            {
                Transaction tx = db.beginTx();
                return tx.acquireWriteLock( resource );
            }
        };
    }
    
    @Test
    public void nestedTransactionCanAcquireLocksFromTransactionObject() throws Exception
    {
        // given
        Node resource = createNode();
        Transaction realTx = db.beginTx();
        Transaction nestedTx = db.beginTx();
        assertNotSame( realTx, nestedTx );
        OtherThreadExecutor<Void> otherTx = new OtherThreadExecutor<>( "other thread", null );
        
        // when
        Lock lock = nestedTx.acquireWriteLock( resource );
        Future<Lock> future = otherTx.executeDontWait( acquireWriteLock( resource ) );
        otherTx.waitUntilWaiting();
        
        // then
        try
        {
            future.get( 1, SECONDS );
            fail( "The nested transaction seems to not have acquired the lock" );
        }
        catch ( TimeoutException e )
        {   // Good
        }
        lock.release();
        assertNotNull( future.get() );
        otherTx.close();
    }

    private Node createNode()
    {
        try(Transaction tx = db.beginTx())
        {
            Node n = db.createNode();
            tx.success();
            return n;
        }
    }
}

@@ -827,16 +827,64 @@
 .core;%0A%0A
+import java.util.concurrent.ExecutionException;%0A
 import j
@@ -2618,32 +2618,33 @@
 = createNode();%0A
+%0A
         Transact
@@ -2647,20 +2647,21 @@
 saction 
-real
+outer
 Tx = db.
@@ -2743,12 +2743,13 @@
 me( 
-real
+outer
 Tx, 
@@ -2760,23 +2760,30 @@
 edTx );%0A
+%0A
        
+ try (
  OtherTh
@@ -2807,17 +2807,21 @@
 %3E otherT
-x
+hread
  = new O
@@ -2864,17 +2864,18 @@
 , null )
-;
+ )
 %0A       
@@ -2867,32 +2867,33 @@
 ull ) )%0A        
+%7B
 %0A        // when
@@ -2889,16 +2889,20 @@
     
+    
 // when%0A
     
@@ -2893,24 +2893,28 @@
     // when%0A
+    
         Lock
@@ -2968,16 +2968,20 @@
         
+  
+  
 Future%3CL
@@ -2998,73 +2998,205 @@
 e = 
-otherTx.executeDontWait( acquireWriteLock( reso
+tryToAcquireSameLockOnAnotherThread( resource, otherThread );%0A%0A            // then%0A            acquireOnOtherThreadTimesOut( fut
 ur
-c
 e )
- 
+;%0A%0A            // and when%0A            lock.release(
 );%0A
+%0A
         
 othe
@@ -3195,33 +3195,59 @@
     
-otherTx.waitUntilWaiting(
+    //then%0A            assertNotNull( future.get() 
 );%0A 
@@ -3257,24 +3257,139 @@
     
+%7D
 %0A    
+%7D%0A%0A
     
-// then
+private void acquireOnOtherThreadTimesOut( Future%3CLock%3E future ) throws InterruptedException, ExecutionException%0A    %7B
 %0A   
@@ -3612,90 +3612,310 @@
     
+%7D%0A%0A
     
-lock.release();
+private Future%3CLock%3E tryToAcquireSameLockOnAnotherThread( Node resource, OtherThreadExecutor%3CVoid%3E otherThread ) throws TimeoutException
 %0A    
+%7B%0A
     
-assertNotNull( future.get() );%0A        otherTx.close()
+    Future%3CLock%3E future = otherThread.executeDontWait( acquireWriteLock( resource ) );%0A        otherThread.waitUntilWaiting();%0A        return future
 ;%0A  

Make NestedTransactionLocksIT more readable.