package com.vladmihalcea.hibernate.masterclass.laboratory.cache;

import com.vladmihalcea.hibernate.masterclass.laboratory.util.AbstractPostgreSQLIntegrationTest;
import org.hibernate.EmptyInterceptor;
import org.hibernate.Interceptor;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.junit.Before;
import org.junit.Test;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;


/**
 * NonStrictReadWriteCacheConcurrencyStrategyWithConcurrentUpdateTest - Test to check CacheConcurrencyStrategy.NONSTRICT_READ_WRITE
 *
 * @author Vlad Mihalcea
 */
public class NonStrictReadWriteCacheConcurrencyStrategyWithConcurrentUpdateTest extends AbstractPostgreSQLIntegrationTest {

    @Override
    protected Class<?>[] entities() {
        return new Class<?>[] {
                Repository.class
        };
    }

    private AtomicBoolean applyInterceptor = new AtomicBoolean();
    private final CountDownLatch endLatch = new CountDownLatch(1);

    @Override
    protected Interceptor interceptor() {
        return new EmptyInterceptor() {
            @Override
            public void beforeTransactionCompletion(Transaction tx) {
                if(applyInterceptor.get()) {
                    LOGGER.info("Fetch Repository from another transaction");
                    assertFalse(getSessionFactory().getCache().containsEntity(Repository.class, 1L));
                    executeSync(() -> {
                        Session _session = getSessionFactory().openSession();
                        Repository repository = (Repository) _session.get(Repository.class, 1L);
                        LOGGER.info("Cached Repository from Bob's transaction {}", repository);
                        _session.close();
                        endLatch.countDown();
                    });
                    assertTrue(getSessionFactory().getCache().containsEntity(Repository.class, 1L));
                }
            }
        };
    }

    @Override
    protected Properties getProperties() {
        Properties properties = super.getProperties();
        properties.put("hibernate.cache.use_second_level_cache", Boolean.TRUE.toString());
        properties.put("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.EhCacheRegionFactory");
        return properties;
    }

    @Before
    public void init() {
        super.init();
        doInTransaction(session -> {
            Repository repository = new Repository("Hibernate-Master-Class");
            session.persist(repository);
        });
    }

    @Test
    public void testRepositoryEntityUpdate() throws InterruptedException {
        LOGGER.info("Read-write entities are write-through on updating");
        doInTransaction(session -> {
            LOGGER.info("Load and modify Repository from Alice's transaction");
            Repository repository = (Repository) session.get(Repository.class, 1L);
            assertTrue(getSessionFactory().getCache().containsEntity(Repository.class, 1L));
            repository.setName("High-Performance Hibernate");
            applyInterceptor.set(true);
        });
        endLatch.await();
        assertFalse(getSessionFactory().getCache().containsEntity(Repository.class, 1L));
        doInTransaction(session -> {
            applyInterceptor.set(false);
            Repository repository = (Repository) session.get(Repository.class, 1L);
            LOGGER.info("Cached Repository from Alice's transaction {}", repository);
        });
    }

    /**
     * Repository - Repository
     *
     * @author Vlad Mihalcea
     */
    @Entity(name = "repository")
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    public static class Repository {

        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;

        private String name;

        public Repository() {
        }

        public Repository(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        @Override
        public String toString() {
            return "Repository{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
}

@@ -1248,24 +1248,25 @@
 cBoolean();%0A
+%0A
     private 
@@ -1329,78 +1329,44 @@
     
-@Override%0A    protected Interceptor interceptor() %7B%0A        return new
+private class BobTransaction extends
  Emp
@@ -1378,18 +1378,16 @@
 erceptor
-()
  %7B%0A     
@@ -1385,28 +1385,24 @@
 r %7B%0A        
-  
-  
 @Override%0A  
@@ -1395,28 +1395,24 @@
   @Override%0A
-    
         publ
@@ -1477,20 +1477,16 @@
         
-  
-  
 if(apply
@@ -1502,28 +1502,24 @@
 or.get()) %7B%0A
-    
             
@@ -1592,28 +1592,24 @@
             
- 
-   
 assertFalse(
@@ -1630,32 +1630,53 @@
 ory().getCache()
+%0A                    
 .containsEntity(
@@ -1715,20 +1715,16 @@
         
-    
 executeS
@@ -1755,20 +1755,16 @@
         
-  
-  
 Session 
@@ -1821,36 +1821,32 @@
                 
-    
 Repository repos
@@ -1865,16 +1865,40 @@
 ository)
+%0A                       
  _sessio
@@ -1938,36 +1938,32 @@
                 
- 
-   
 LOGGER.info(%22Cac
@@ -1996,32 +1996,56 @@
 transaction %7B%7D%22,
+%0A                       
  repository);%0A  
@@ -2054,36 +2054,32 @@
                 
-   
- 
 _session.close()
@@ -2076,28 +2076,24 @@
 on.close();%0A
-    
             
@@ -2134,36 +2134,28 @@
             
-   
- 
 %7D);%0A
-    
             
@@ -2191,32 +2191,53 @@
 ory().getCache()
+%0A                    
 .containsEntity(
@@ -2260,32 +2260,38 @@
 , 1L));%0A        
+    %7D%0A
         %7D%0A      
@@ -2288,35 +2288,110 @@
   %7D%0A    
+%7D%0A%0A
     
-    %7D%0A        %7D
+@Override%0A    protected Interceptor interceptor() %7B%0A        return new BobTransaction()
 ;%0A    %7D%0A
@@ -3067,82 +3067,8 @@
 n %7B%0A
-        LOGGER.info(%22Read-write entities are write-through on updating%22);%0A
     
@@ -3155,33 +3155,8 @@
 tory
- from Alice's transaction
 %22);%0A
@@ -3195,32 +3195,48 @@
 y = (Repository)
+%0A               
  session.get(Rep
@@ -3300,32 +3300,49 @@
 ory().getCache()
+%0A                
 .containsEntity(
@@ -3555,16 +3555,29 @@
 tCache()
+%0A            
 .contain
@@ -3734,16 +3734,32 @@
 ository)
+%0A               
  session
@@ -3833,33 +3833,8 @@
 ory 
-from Alice's transaction 
 %7B%7D%22,

Reduce identation