package igniteUnlockTest;

import org.apache.ignite.Ignition;
import org.apache.ignite.configuration.DataRegionConfiguration;
import org.apache.ignite.configuration.DataStorageConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder;

import java.util.Arrays;
import java.util.List;

import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteLock;

/**
 * Test demonstrating the failure to unlock on ignite node exit. Creates a node then second node which successfully joins the cluster. the 
 * second node then creates reentrant lock on a node, locks it and allows the thread holding the lock to die. The node is closed as 
 * demonstrated by:
 * 
 *		Topology snapshot [ver=2, servers=2, clients=0, CPUs=4, offheap=3.1GB, heap=0.5GB]
 *
 * changing to:
 * 
 * 		Topology snapshot [ver=3, servers=1, clients=0, CPUs=4, offheap=3.1GB, heap=0.5GB]
 * 		Ignite node stopped OK [name=INSTANCE2, uptime=00:00:00.132]
 * 
 * But when checked, on the remaining node, the lock is still held.
 * 
 * Tested under Eclipse on Ignite 2.5.0. Problem seen on both RHL and Windows.
 */
public class UnlockTest 
{
	public final static String LOCKNAME="LOCKNAME";
	
    public static void main( String[] args )
    {
        System.out.println( "Starting test" );

        // Make a uniquely named  ignite node.   
        IgniteConfiguration config1 = new DefaultIgniteConfig();
        config1.setIgniteInstanceName("INSTANCE1");
        
        // Start node. No caches required for this test.
        Ignite node1 = Ignition.start(config1);
        
        // Make a lock
        IgniteLock lock = node1.reentrantLock(LOCKNAME, true, false, true);
        
        // Check is initially unlocked
        if (lock.isLocked()) {
        	System.out.println("Is initially locked");
        }
        
        // Create a remote thread. 
        TestThread thread = new TestThread(LOCKNAME);
         
        // Run and exit thread. Will get a handle to the lock and lock it.
        thread.start();
        
        try {
        // Wait for thread to close
        thread.join();
        
        // Ignite node should be closed now. But wait a while to be sure
        Thread.sleep(5000);
        }
        catch (Exception e) {
        	System.out.println("Problem waiting for thread" + e.getMessage());
        }
        
        // Check that the lock is unlocked
        if (lock.isLocked()) {
        	// This should not be printed but it is.
        	System.out.println("ERROR ; Lock is not released on exit");
        }
        
        // Close node
        node1.close();
        
        System.out.println( "Test done" );
    }
    
    // Remote thread that takes a lock.
    private static class TestThread extends Thread {
    	private Ignite node2;
    	private String lockName;
    	
    	public TestThread(String lockName) {
    		// Remember lock name.
    		this.lockName = lockName;
    		
    		// Start a new, uniquely named, ignite node for the thread, 
            IgniteConfiguration config2 = new DefaultIgniteConfig();
            config2.setIgniteInstanceName("INSTANCE2");
            
            // Start an ignite node. No caches required.
            node2 = Ignition.start(config2);	
    	}
    	
    	public void run() {
    		// Get a handle to the lock and lock it. 
    		// Note : failoverSafe=true. So it should release when the node is closed.
            IgniteLock lock = node2.reentrantLock(lockName, true, false, true);
            lock.lock();
            
            // Check that the lock is locked by this thread.
            if (!lock.isLocked()) {
            	System.out.println("Is not locked");
            }
            if (!lock.isHeldByCurrentThread()){
            	System.out.println("Is held by worng thread");
            }
            
                     
    		// Should be able to run off the end, or crash, and get the lock released,
            // int i = 0;
            // int a = 1/i;
            
            // Even if we explicitly close the node the lock is still held.
    		node2.close();
    	}
    	
    }
    
    // A simple, canned, ignite config.
 	private static class DefaultIgniteConfig extends IgniteConfiguration {
 		public DefaultIgniteConfig() {
 			// Some basic config.
 			setIgniteInstanceName("IGNITE_LOCK_TEST");
 			// Need a server to connect to. So be a server.
 			setClientMode(false);
 			setPeerClassLoadingEnabled(true);

 			// Set up node discovery
 			TcpDiscoverySpi discoSpi = new TcpDiscoverySpi();
 			TcpDiscoveryMulticastIpFinder finder = new TcpDiscoveryMulticastIpFinder();
 			List<String> addresses = Arrays.asList("127.0.0.1:47500..47502");
 			finder.setAddresses(addresses);
 			discoSpi.setIpFinder(finder);
 			setDiscoverySpi(discoSpi);

 			// Set up data storage
 			DataStorageConfiguration storeConfig = new DataStorageConfiguration();
 			DataRegionConfiguration regionConfig = storeConfig.getDefaultDataRegionConfiguration();
 			regionConfig.setName("IGNITE_LOCK_REGION");
 			
 			// For locking should not need persistence.
 			regionConfig.setPersistenceEnabled(false);
 			setDataStorageConfiguration(storeConfig);
 			
 			// Don't need any caches.
 		}
 	}
}
