/*
* 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 org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.ecr.core.NXCore;
import org.eclipse.ecr.core.api.CoreSession;
import org.eclipse.ecr.core.api.DocumentModel;
import org.eclipse.ecr.core.api.DocumentRef;
import org.eclipse.ecr.core.api.PathRef;
import org.eclipse.ecr.core.api.impl.DocumentModelImpl;
import org.eclipse.ecr.core.api.security.SecurityConstants;
import org.eclipse.ecr.core.event.EventService;
import org.eclipse.ecr.core.event.EventTransactionListener;
import org.eclipse.ecr.core.model.Repository;
import org.eclipse.ecr.core.storage.sql.testlib.DatabaseH2;
import org.eclipse.ecr.core.storage.sql.testlib.TXSQLRepositoryTestCase;
import org.eclipse.ecr.runtime.api.Framework;
import org.eclipse.ecr.runtime.transaction.TransactionHelper;
import org.eclipse.ecr.runtime.transaction.TransactionRuntimeException;
public class TestSQLRepositoryJTAJCA extends TXSQLRepositoryTestCase {
/**
* Test that connection sharing allows use of several sessions at the same
* time.
*/
public void testSessionSharing() throws Exception {
if (!hasPoolingConfig()) {
return;
}
Repository repo = NXCore.getRepositoryService().getRepositoryManager().getRepository(REPOSITORY_NAME);
assertEquals(1, repo.getActiveSessionsCount());
CoreSession session2 = openSessionAs(SecurityConstants.ADMINISTRATOR);
assertEquals(1, repo.getActiveSessionsCount());
try {
DocumentModel doc = new DocumentModelImpl("/", "doc", "Document");
doc = session.createDocument(doc);
session.save();
// check that this is immediately seen from other connection
// (underlying ManagedConnection is the same)
assertTrue(session2.exists(new PathRef("/doc")));
} finally {
closeSession(session2);
}
assertEquals(1, repo.getActiveSessionsCount());
}
/**
* Test that a commit implicitly does a save.
*/
public void testSaveOnCommit() throws Exception {
if (!hasPoolingConfig()) {
return;
}
// first transaction
DocumentModel doc = new DocumentModelImpl("/", "doc", "Document");
doc = session.createDocument(doc);
// let commit do an implicit save
TransactionHelper.commitOrRollbackTransaction(); // release cx
TransactionHelper.startTransaction();
openSession(); // reopen cx and hold open
Thread t = new Thread() {
@Override
public void run() {
try {
TransactionHelper.startTransaction();
CoreSession session2;
session2 = openSessionAs(SecurityConstants.ADMINISTRATOR);
try {
assertTrue(session2.exists(new PathRef("/doc")));
} finally {
closeSession(session2);
TransactionHelper.commitOrRollbackTransaction();
}
} catch (Exception e) {
fail(e.toString());
}
}
};
t.start();
t.join();
}
/**
* Test that the TransactionalCoreSessionWrapper does its job.
*/
public void testRollbackOnException() throws Exception {
if (!(database instanceof DatabaseH2)) {
// no pooling conf available
return;
}
assertTrue(TransactionHelper.isTransactionActive());
try {
session.getDocument(new PathRef("/nosuchdoc"));
fail("Missing document should throw");
} catch (Exception e) {
// ok
}
// tx still active because CoreSession.getDocument is marked
// @NoRollbackOnException
assertTrue(TransactionHelper.isTransactionActive());
closeSession();
TransactionHelper.commitOrRollbackTransaction();
TransactionHelper.startTransaction();
openSession();
assertTrue(TransactionHelper.isTransactionActive());
DocumentModel doc = new DocumentModelImpl("/nosuchdir", "doc", "Document");
try {
session.createDocument(doc);
fail("Missing parent should throw");
} catch (Exception e) {
// ok
}
// tx not active anymore because CoreSession.createDocument is not
// marked @NoRollbackOnException
assertTrue(TransactionHelper.isTransactionMarkedRollback());
}
protected static class HelperEventTransactionListener implements EventTransactionListener {
public boolean committed;
@Override
public void transactionStarted() {
}
@Override
public void transactionCommitted() {
committed = true;
}
@Override
public void transactionRollbacked() {
}
}
public void testAfterCompletion() {
EventService eventService = Framework.getLocalService(EventService.class);
HelperEventTransactionListener listener = new HelperEventTransactionListener();
eventService.addTransactionListener(listener);
assertFalse(listener.committed);
TransactionHelper.commitOrRollbackTransaction();
assertTrue(listener.committed);
}
protected static final Log log = LogFactory.getLog(TestSQLRepositoryJTAJCA.class);
/**
* Testing that if 2 modifications are done at the same time on the same
* document on 2 separate transactions, one is rejected
* (TransactionRuntimeException)
*/
public void XXX_TODO_testConcurrentModification() throws Exception {
if (!hasPoolingConfig()) {
return;
}
// first transaction
DocumentModel doc = session.createDocumentModel("/", "doc", "Note");
doc.getProperty("dc:title").setValue("initial");
doc = session.createDocument(doc);
// let commit do an implicit save
closeSession(session);
TransactionHelper.commitOrRollbackTransaction(); // release cx
final DocumentRef ref = new PathRef("/doc");
TransactionHelper.startTransaction();
openSession();
doc = session.getDocument(ref);
doc.getProperty("dc:title").setValue("first");
session.saveDocument(doc);
Thread t = new Thread() {
@Override
public void run() {
try {
TransactionHelper.startTransaction();
CoreSession session2;
session2 = openSessionAs(SecurityConstants.ADMINISTRATOR);
try {
DocumentModel doc = session2.getDocument(ref);
doc.getProperty("dc:title").setValue("second update");
session2.saveDocument(doc);
} catch (Exception e) {
log.error("Catched error while setting title", e);
} finally {
TransactionHelper.commitOrRollbackTransaction();
closeSession(session2);
}
} catch (Exception e) {
fail(e.toString());
}
}
};
t.start();
t.join();
try {
TransactionHelper.commitOrRollbackTransaction(); // release cx
fail("expected TransactionRuntimeException");
} catch (TransactionRuntimeException e) {
// expected
}
}
}