From c4cdf546d76a4e412a6c658ec16c220a53fcc693 Mon Sep 17 00:00:00 2001 From: Andreas Meingast Date: Sun, 16 Jun 2013 15:53:20 +0200 Subject: [PATCH] Implemented ConnectionFactory for RepositoryManagers --- README.markdown | 2 +- pom.xml | 5 + .../spring/RepositoryConnectionFactory.java | 202 ++++++++++++++++++ .../RepositoryManagerConnectionFactory.java | 112 ++++++++++ .../spring/SesameConnectionFactory.java | 193 +---------------- ...onTest.java => RepositoryManagerTest.java} | 20 +- src/test/resources/applicationContext.xml | 12 +- 7 files changed, 346 insertions(+), 200 deletions(-) create mode 100644 src/main/java/org/openrdf/spring/RepositoryConnectionFactory.java create mode 100644 src/main/java/org/openrdf/spring/RepositoryManagerConnectionFactory.java rename src/test/java/org/openrdf/spring/{SesameTransactionTest.java => RepositoryManagerTest.java} (81%) diff --git a/README.markdown b/README.markdown index 36b9c62..9abb91a 100644 --- a/README.markdown +++ b/README.markdown @@ -27,7 +27,7 @@ Wiring up a simple in-memory repository to the transaction manager: - + diff --git a/pom.xml b/pom.xml index 791b55b..bbb2c7b 100644 --- a/pom.xml +++ b/pom.xml @@ -75,6 +75,11 @@ sesame-query ${sesame.version} + + org.openrdf.sesame + sesame-repository-manager + ${sesame.version} + org.openrdf.sesame sesame-queryparser-sparql diff --git a/src/main/java/org/openrdf/spring/RepositoryConnectionFactory.java b/src/main/java/org/openrdf/spring/RepositoryConnectionFactory.java new file mode 100644 index 0000000..e9f7a81 --- /dev/null +++ b/src/main/java/org/openrdf/spring/RepositoryConnectionFactory.java @@ -0,0 +1,202 @@ +package org.openrdf.spring; + +import org.openrdf.repository.Repository; +import org.openrdf.repository.RepositoryConnection; +import org.openrdf.repository.RepositoryException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.transaction.TransactionSystemException; + +/** + *

{@link RepositoryConnectionFactory} handles connections to the corresponding {@link Repository} and manages + * the transaction state (represented by {@link SesameTransactionObject}).

+ *

+ *

This class provides methods to access transactional connections from the outside and is typically + * the only class that library users interact with.

+ * + * @author ameingast@gmail.com + */ +public class RepositoryConnectionFactory implements DisposableBean, SesameConnectionFactory { + private final ThreadLocal localTransactionObject; + + private Repository repository; + + public RepositoryConnectionFactory(Repository repository) { + this.repository = repository; + localTransactionObject = new ThreadLocal(); + } + + /** + *

Retrieves the connection for the current transaction. This method may be called at any time as long as a + * transaction is active and will return the same connection-handle to the repository in the same + * transaction context (which is thread-local).

+ * + * @return the {@link RepositoryConnection} + * @throws SesameTransactionException if + *
    + *
  • No transaction is active
  • + *
+ */ + @Override + public RepositoryConnection getConnection() { + SesameTransactionObject sesameTransactionObject = localTransactionObject.get(); + + if (sesameTransactionObject == null) { + throw new SesameTransactionException("No transaction active"); + } + + RepositoryConnection repositoryConnection = sesameTransactionObject.getRepositoryConnection(); + + try { + if (!repositoryConnection.isOpen()) { + throw new SesameTransactionException("Connection closed during transaction"); + } + } catch (RepositoryException e) { + throw new SesameTransactionException(e); + } + + return repositoryConnection; + } + + /** + *

Closes the connection and cleans up the (thread-local) state for the current transaction.

+ *

+ *

This method should not be called manually, since the connection is managed by the + * {@link SesameTransactionManager}.

+ *

+ * + * @throws SesameTransactionException if + *
    + *
  • No transaction is active
  • + *
  • The connection could not be closed
  • + *
+ */ + @Override + public void closeConnection() { + SesameTransactionObject sesameTransactionObject = null; + RepositoryConnection repositoryConnection = null; + + try { + sesameTransactionObject = localTransactionObject.get(); + + if (sesameTransactionObject == null) { + throw new SesameTransactionException("No transaction active"); + } + + repositoryConnection = sesameTransactionObject.getRepositoryConnection(); + + try { + if (!repositoryConnection.isOpen()) { + throw new SesameTransactionException("Connection closed during transaction"); + } + } catch (RepositoryException e) { + throw new SesameTransactionException(e); + } + } finally { + if (sesameTransactionObject != null && repositoryConnection != null) { + try { + repositoryConnection.close(); + } catch (RepositoryException e) { + throw new SesameTransactionException(e); + } + + localTransactionObject.remove(); + } + } + } + + /** + *

Shuts down the {@link Repository} if it was initialized before.

+ * + * @throws Exception {@see Repository#shutDown} + */ + @Override + public void destroy() throws Exception { + if (repository != null && repository.isInitialized()) { + try { + repository.shutDown(); + } finally { + repository = null; + } + } + } + + /** + *

Creates a new {@link SesameTransactionObject}, connects the created object + * to the corresponding {@link Repository} and disables auto-commit on the connection.

+ *

+ *

This method should only be called by {@link SesameTransactionManager}.

+ * + * @return the created transaction object representing the transaction state. + * @throws RepositoryException {@see Repository#getConnection} + */ + @Override + public SesameTransactionObject createTransaction() throws RepositoryException { + RepositoryConnection connection = repository.getConnection(); + connection.setAutoCommit(false); + + SesameTransactionObject sesameTransactionObject = new SesameTransactionObject(connection); + localTransactionObject.set(sesameTransactionObject); + + return sesameTransactionObject; + } + + /** + *

Ends the active transaction by either rolling-back or committing the changes to the {@link Repository} + * depending on the rollback-flag.

+ *

+ *

This method should only be called by {@link SesameTransactionManager}.

+ * + * @param rollback if true the current transaction is rolled back, if false the pending + * changes on the connection are committed to the {@link Repository}. + * @throws RepositoryException if + *
    + *
  • The changes could not be rolled back
  • + *
  • The changes could not be committed
  • + *
+ * @throws SesameTransactionException if + *
    + *
  • No transaction is active
  • + *
  • The connection could not be closed
  • + *
+ */ + @Override + public void endTransaction(boolean rollback) throws RepositoryException { + SesameTransactionObject sesameTransactionObject = localTransactionObject.get(); + + if (sesameTransactionObject == null) { + throw new TransactionSystemException("No transaction active"); + } + + RepositoryConnection repositoryConnection = sesameTransactionObject.getRepositoryConnection(); + + if (!repositoryConnection.isOpen()) { + throw new SesameTransactionException("Connection closed during transaction"); + } + + if (rollback) { + repositoryConnection.rollback(); + } else { + repositoryConnection.commit(); + } + } + + /** + *

Retrieves the current transaction state.

+ *

+ *

This method should only be called by {@link SesameTransactionManager}.

+ * + * @return The current transaction state in form of a {@link SesameTransactionObject}. + */ + @Override + public SesameTransactionObject getLocalTransactionObject() { + return localTransactionObject.get(); + } + + @Override + public String toString() { + return "RepositoryConnectionFactory{" + + "repository=" + repository + + ", localTransactionObject=" + localTransactionObject + + '}'; + } +} diff --git a/src/main/java/org/openrdf/spring/RepositoryManagerConnectionFactory.java b/src/main/java/org/openrdf/spring/RepositoryManagerConnectionFactory.java new file mode 100644 index 0000000..7863334 --- /dev/null +++ b/src/main/java/org/openrdf/spring/RepositoryManagerConnectionFactory.java @@ -0,0 +1,112 @@ +package org.openrdf.spring; + +import org.openrdf.repository.Repository; +import org.openrdf.repository.RepositoryConnection; +import org.openrdf.repository.RepositoryException; +import org.openrdf.repository.config.RepositoryConfigException; +import org.openrdf.repository.manager.RepositoryManager; + +/** + *

{@link RepositoryManagerConnectionFactory} handles connections to a {@link RepositoryManager} and manages + * the transaction state (represented by {@link SesameTransactionObject}).

+ *

+ *

The {@link RepositoryManager}'s repository-id is set via the property + * {@link RepositoryManagerConnectionFactory#setLocalRepositoryId(String)}/ + * {@link org.openrdf.spring.RepositoryManagerConnectionFactory#getLocalRepositoryId()}. + *

+ *

It supports a single repository-id per thread; i.e. you can use the {@link RepositoryManagerConnectionFactory} + * with different @{link Repository}s in different threads. + *

+ *

This class provides methods to access transactional connections from the outside and is typically + * the only class that library users interact with.

+ * + * @author ameingast@gmail.com + */ +public class RepositoryManagerConnectionFactory implements SesameConnectionFactory { + private final RepositoryManager repositoryManager; + + private ThreadLocal localRepositoryId; + + private ThreadLocal localConnectionFactory; + + public RepositoryManagerConnectionFactory(RepositoryManager repositoryManager) { + this.repositoryManager = repositoryManager; + localRepositoryId = new ThreadLocal(); + localConnectionFactory = new ThreadLocal(); + } + + @Override + public RepositoryConnection getConnection() { + initializeLocalConnectionFactory(); + + return localConnectionFactory.get().getConnection(); + } + + private void initializeLocalConnectionFactory() { + if (localConnectionFactory.get() == null) { + if (localRepositoryId.get() == null || localRepositoryId.get().isEmpty()) { + throw new RuntimeException("Local repository-id has not been initialized"); + } + + try { + Repository repository = repositoryManager.getRepository(localRepositoryId.get()); + RepositoryConnectionFactory repositoryConnectionFactory = new RepositoryConnectionFactory(repository); + + localConnectionFactory.set(repositoryConnectionFactory); + } catch (RepositoryException e) { + throw new RuntimeException(e); + } catch (RepositoryConfigException e) { + throw new RuntimeException(e); + } + } + } + + @Override + public void closeConnection() { + try { + initializeLocalConnectionFactory(); + + localConnectionFactory.get().closeConnection(); + } finally { + localConnectionFactory.remove(); + } + } + + @Override + public SesameTransactionObject createTransaction() throws RepositoryException { + initializeLocalConnectionFactory(); + + return localConnectionFactory.get().createTransaction(); + } + + @Override + public void endTransaction(boolean rollback) throws RepositoryException { + initializeLocalConnectionFactory(); + + localConnectionFactory.get().endTransaction(rollback); + } + + @Override + public SesameTransactionObject getLocalTransactionObject() { + initializeLocalConnectionFactory(); + + return localConnectionFactory.get().getLocalTransactionObject(); + } + + public String getLocalRepositoryId() { + return localRepositoryId.get(); + } + + public void setLocalRepositoryId(String repositoryId) { + this.localRepositoryId.set(repositoryId); + } + + @Override + public String toString() { + return "RepositoryManagerConnectionFactory{" + + "repositoryManager=" + repositoryManager + + ", repositoryId='" + localRepositoryId + '\'' + + ", connectionFactory=" + localConnectionFactory + + '}'; + } +} diff --git a/src/main/java/org/openrdf/spring/SesameConnectionFactory.java b/src/main/java/org/openrdf/spring/SesameConnectionFactory.java index 3826a4f..8c882c6 100644 --- a/src/main/java/org/openrdf/spring/SesameConnectionFactory.java +++ b/src/main/java/org/openrdf/spring/SesameConnectionFactory.java @@ -1,197 +1,16 @@ package org.openrdf.spring; -import org.openrdf.repository.Repository; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.transaction.TransactionSystemException; -/** - *

{@link SesameConnectionFactory} handles connections to the corresponding {@link Repository} and manages - * the transaction state (represented by {@link SesameTransactionObject}).

- *

- *

This class provides methods to access transactional connections from the outside and is typically - * the only class that library users interact with.

- * - * @author ameingast@gmail.com - */ -public class SesameConnectionFactory implements DisposableBean { - private final ThreadLocal localTransactionObject; +public interface SesameConnectionFactory { + RepositoryConnection getConnection(); - private Repository repository; + void closeConnection(); - public SesameConnectionFactory(Repository repository) { - this.repository = repository; - localTransactionObject = new ThreadLocal(); - } + SesameTransactionObject createTransaction() throws RepositoryException; - /** - *

Retrieves the connection for the current transaction. This method may be called at any time as long as a - * transaction is active and will return the same connection-handle to the repository in the same - * transaction context (which is thread-local).

- * - * @return the {@link RepositoryConnection} - * @throws SesameTransactionException if - *
    - *
  • No transaction is active
  • - *
- */ - public RepositoryConnection getConnection() { - SesameTransactionObject sesameTransactionObject = localTransactionObject.get(); + void endTransaction(boolean rollback) throws RepositoryException; - if (sesameTransactionObject == null) { - throw new SesameTransactionException("No transaction active"); - } - - RepositoryConnection repositoryConnection = sesameTransactionObject.getRepositoryConnection(); - - try { - if (!repositoryConnection.isOpen()) { - throw new SesameTransactionException("Connection closed during transaction"); - } - } catch (RepositoryException e) { - throw new SesameTransactionException(e); - } - - return repositoryConnection; - } - - /** - *

Closes the connection and cleans up the (thread-local) state for the current transaction.

- *

- *

This method should not be called manually, since the connection is managed by the - * {@link SesameTransactionManager}.

- *

- * - * @throws SesameTransactionException if - *
    - *
  • No transaction is active
  • - *
  • The connection could not be closed
  • - *
- */ - void closeConnection() { - SesameTransactionObject sesameTransactionObject = null; - RepositoryConnection repositoryConnection = null; - - try { - sesameTransactionObject = localTransactionObject.get(); - - if (sesameTransactionObject == null) { - throw new SesameTransactionException("No transaction active"); - } - - repositoryConnection = sesameTransactionObject.getRepositoryConnection(); - - try { - if (!repositoryConnection.isOpen()) { - throw new SesameTransactionException("Connection closed during transaction"); - } - } catch (RepositoryException e) { - throw new SesameTransactionException(e); - } - } finally { - if (sesameTransactionObject != null && repositoryConnection != null) { - try { - repositoryConnection.close(); - } catch (RepositoryException e) { - throw new SesameTransactionException(e); - } - - localTransactionObject.remove(); - } - } - } - - /** - *

Shuts down the {@link Repository} if it was initialized before.

- * - * @throws Exception {@see Repository#shutDown} - */ - @Override - public void destroy() throws Exception { - if (repository != null && repository.isInitialized()) { - try { - repository.shutDown(); - } finally { - repository = null; - } - } - } - - /** - *

Creates a new {@link SesameTransactionObject}, connects the created object - * to the corresponding {@link Repository} and disables auto-commit on the connection.

- *

- *

This method should only be called by {@link SesameTransactionManager}.

- * - * @return the created transaction object representing the transaction state. - * @throws RepositoryException {@see Repository#getConnection} - */ - SesameTransactionObject createTransaction() throws RepositoryException { - RepositoryConnection connection = repository.getConnection(); - connection.setAutoCommit(false); - - SesameTransactionObject sesameTransactionObject = new SesameTransactionObject(connection); - localTransactionObject.set(sesameTransactionObject); - - return sesameTransactionObject; - } - - /** - *

Ends the active transaction by either rolling-back or committing the changes to the {@link Repository} - * depending on the rollback-flag.

- *

- *

This method should only be called by {@link SesameTransactionManager}.

- * - * @param rollback if true the current transaction is rolled back, if false the pending - * changes on the connection are committed to the {@link Repository}. - * @throws RepositoryException if - *
    - *
  • The changes could not be rolled back
  • - *
  • The changes could not be committed
  • - *
- * @throws SesameTransactionException if - *
    - *
  • No transaction is active
  • - *
  • The connection could not be closed
  • - *
- */ - void endTransaction(boolean rollback) throws RepositoryException { - SesameTransactionObject sesameTransactionObject = localTransactionObject.get(); - - if (sesameTransactionObject == null) { - throw new TransactionSystemException("No transaction active"); - } - - RepositoryConnection repositoryConnection = sesameTransactionObject.getRepositoryConnection(); - - if (!repositoryConnection.isOpen()) { - throw new SesameTransactionException("Connection closed during transaction"); - } - - if (rollback) { - repositoryConnection.rollback(); - } else { - repositoryConnection.commit(); - } - } - - /** - *

Retrieves the current transaction state.

- *

- *

This method should only be called by {@link SesameTransactionManager}.

- * - * @return The current transaction state in form of a {@link SesameTransactionObject}. - */ - SesameTransactionObject getLocalTransactionObject() { - return localTransactionObject.get(); - } - - @Override - public String toString() { - return "SesameConnectionFactory{" + - "repository=" + repository + - ", localTransactionObject=" + localTransactionObject + - '}'; - } + SesameTransactionObject getLocalTransactionObject(); } diff --git a/src/test/java/org/openrdf/spring/SesameTransactionTest.java b/src/test/java/org/openrdf/spring/RepositoryManagerTest.java similarity index 81% rename from src/test/java/org/openrdf/spring/SesameTransactionTest.java rename to src/test/java/org/openrdf/spring/RepositoryManagerTest.java index fa907a0..d51fb09 100644 --- a/src/test/java/org/openrdf/spring/SesameTransactionTest.java +++ b/src/test/java/org/openrdf/spring/RepositoryManagerTest.java @@ -21,26 +21,26 @@ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "/applicationContext.xml") -public class SesameTransactionTest { +public class RepositoryManagerTest { @Autowired - protected SesameConnectionFactory sesameConnectionFactory; + protected SesameConnectionFactory repositoryConnectionFactory; @Test(expected = SesameTransactionException.class) public void testFactoryDoesNotCreateConnection() throws RepositoryException { - sesameConnectionFactory.getConnection(); + repositoryConnectionFactory.getConnection(); } @Test @Transactional public void testTransactionCreatesConnection() throws RepositoryException { - RepositoryConnection currentConnection = sesameConnectionFactory.getConnection(); + RepositoryConnection currentConnection = repositoryConnectionFactory.getConnection(); Assert.assertNotNull(currentConnection); } @Test @Transactional public void testTransactionDisablesAutoCommit() throws RepositoryException { - RepositoryConnection connection = sesameConnectionFactory.getConnection(); + RepositoryConnection connection = repositoryConnectionFactory.getConnection(); Assert.assertFalse(connection.isAutoCommit()); } @@ -48,7 +48,7 @@ public void testTransactionDisablesAutoCommit() throws RepositoryException { @Test @Transactional public void testWrapTransactions() { - RepositoryConnection connection = sesameConnectionFactory.getConnection(); + RepositoryConnection connection = repositoryConnectionFactory.getConnection(); for (RepositoryConnection repositoryConnection : Arrays.asList(transactionScope(), transactionScopeWithoutAnnotation())) { Assert.assertTrue(connection == repositoryConnection); @@ -57,11 +57,11 @@ public void testWrapTransactions() { @Transactional protected RepositoryConnection transactionScope() { - return sesameConnectionFactory.getConnection(); + return repositoryConnectionFactory.getConnection(); } protected RepositoryConnection transactionScopeWithoutAnnotation() { - return sesameConnectionFactory.getConnection(); + return repositoryConnectionFactory.getConnection(); } @Test @@ -72,7 +72,7 @@ public void testWriteData() throws Exception { } protected void assertDataPresent() throws Exception { - RepositoryConnection connection = sesameConnectionFactory.getConnection(); + RepositoryConnection connection = repositoryConnectionFactory.getConnection(); final TupleQuery tupleQuery = connection.prepareTupleQuery(QueryLanguage.SPARQL, "SELECT ?s ?p ?o WHERE { ?s ?p ?o . }"); TupleQueryResult result = tupleQuery.evaluate(); @@ -96,7 +96,7 @@ protected void addData() throws RepositoryException { URI b = f.createURI("http://example.com/b"); URI c = f.createURI("http://example.com/c"); - RepositoryConnection connection = sesameConnectionFactory.getConnection(); + RepositoryConnection connection = repositoryConnectionFactory.getConnection(); connection.add(a, b, c); } } diff --git a/src/test/resources/applicationContext.xml b/src/test/resources/applicationContext.xml index 2f0fdde..88432a7 100644 --- a/src/test/resources/applicationContext.xml +++ b/src/test/resources/applicationContext.xml @@ -22,11 +22,19 @@
- + + + + + + + + + - + \ No newline at end of file