/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Florent Guillaume */ package org.eclipse.ecr.core.storage.sql; import java.util.concurrent.CountDownLatch; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.ecr.core.api.CoreSession; import org.eclipse.ecr.core.api.DocumentModel; import org.eclipse.ecr.core.api.impl.DocumentModelImpl; import org.eclipse.ecr.core.api.security.SecurityConstants; import org.eclipse.ecr.core.storage.sql.testlib.TXSQLRepositoryTestCase; import org.eclipse.ecr.runtime.transaction.TransactionHelper; /** * Tests locking behavior under transaction. Subclass tests with no transaction. */ public class TestSQLRepositoryLocking extends TXSQLRepositoryTestCase { protected static final Log log = LogFactory.getLog(TestSQLRepositoryJTAJCA.class); protected static final String SYSTEM = SecurityConstants.SYSTEM_USERNAME; @SuppressWarnings("deprecation") protected static final String ADMINISTRATOR = SecurityConstants.ADMINISTRATOR; // subclassed to disable transactions protected boolean useTX() { return true; } @Override public void setUp() throws Exception { super.setUp(); if (!useTX()) { closeSession(); TransactionHelper.commitOrRollbackTransaction(); openSession(); } } protected void nextTX() throws Exception { closeSession(); if (useTX()) { TransactionHelper.commitOrRollbackTransaction(); TransactionHelper.startTransaction(); } openSession(); } @SuppressWarnings("deprecation") public void testLocking() throws Exception { if (!hasPoolingConfig()) { return; } DocumentModel root = session.getRootDocument(); DocumentModel doc = new DocumentModelImpl("/", "doc", "File"); doc = session.createDocument(doc); assertNull(doc.getLock()); // old assertNull(doc.getLockInfo()); assertFalse(doc.isLocked()); session.save(); nextTX(); doc = session.getChild(root.getRef(), "doc"); doc.setLock(); assertEquals(ADMINISTRATOR, doc.getLockInfo().getOwner()); assertNotNull(doc.getLockInfo().getCreated()); assertTrue(doc.getLock().startsWith(ADMINISTRATOR + ':')); // old assertTrue(doc.isLocked()); nextTX(); doc = session.getChild(root.getRef(), "doc"); assertEquals(ADMINISTRATOR, doc.getLockInfo().getOwner()); assertNotNull(doc.getLockInfo().getCreated()); assertTrue(doc.getLock().startsWith(ADMINISTRATOR + ':')); // old assertTrue(doc.isLocked()); nextTX(); doc = session.getChild(root.getRef(), "doc"); doc.removeLock(); assertNull(doc.getLockInfo()); assertFalse(doc.isLocked()); nextTX(); doc = session.getChild(root.getRef(), "doc"); assertFalse(doc.isLocked()); } public void testLockingBeforeSave() throws Exception { if (!hasPoolingConfig()) { return; } DocumentModel root = session.getRootDocument(); DocumentModel doc = new DocumentModelImpl("/", "doc", "File"); doc = session.createDocument(doc); doc.setLock(); session.save(); nextTX(); doc = session.getChild(root.getRef(), "doc"); assertTrue(doc.isLocked()); } // check we don't have a SQL-level locking error due to the lock manager // connection reading a row that was written but not yet committed by the // main connection public void testGetLockAfterCreate() throws Exception { if (!hasPoolingConfig()) { return; } DocumentModel doc1 = new DocumentModelImpl("/", "doc1", "File"); doc1 = session.createDocument(doc1); session.save(); // read lock after save (SQL INSERT) assertNull(doc1.getLockInfo()); DocumentModel doc2 = new DocumentModelImpl("/", "doc2", "File"); doc2 = session.createDocument(doc2); session.save(); // set lock after save (SQL INSERT) doc2.setLock(); } protected CountDownLatch threadStartLatch; protected CountDownLatch lockingLatch; protected volatile boolean locked; public void testLockingWithMultipleThreads() throws Exception { if (!hasPoolingConfig()) { return; } DocumentModel root = session.getRootDocument(); DocumentModel doc = new DocumentModelImpl("/", "doc", "File"); doc = session.createDocument(doc); session.save(); nextTX(); doc = session.getChild(root.getRef(), "doc"); assertFalse(doc.isLocked()); // start other thread threadStartLatch = new CountDownLatch(1); lockingLatch = new CountDownLatch(1); Thread t = new Thread() { @Override public void run() { CoreSession session2 = null; try { session2 = openSessionAs(ADMINISTRATOR); DocumentModel root2 = session2.getRootDocument(); DocumentModel doc2 = session2.getChild(root2.getRef(), "doc"); // let main thread continue threadStartLatch.countDown(); // wait main thread trigger lockingLatch.await(); locked = doc2.isLocked(); } catch (Exception e) { throw new RuntimeException(e); } finally { if (session2 != null) { closeSession(session2); } } } }; t.start(); threadStartLatch.await(); doc.setLock(); assertTrue(doc.isLocked()); // trigger other thread check lockingLatch.countDown(); t.join(); assertTrue(locked); } }