Index: src/main/java/org/modeshape/jdbc/JcrConnection.java =================================================================== --- src/main/java/org/modeshape/jdbc/JcrConnection.java (revision 2478) +++ src/main/java/org/modeshape/jdbc/JcrConnection.java (working copy) @@ -44,6 +44,7 @@ import java.util.Map; import java.util.Properties; +import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.nodetype.NodeType; import javax.jcr.query.Query; @@ -408,11 +409,11 @@ public DatabaseMetaData getMetaData() throws SQLException { notClosed(); if (metadata == null) { - try { - metadata = this.getRepositoryDelegate().createMetaData(this); - } catch (RepositoryException e) { - throw new SQLException(e.getLocalizedMessage()); - } + String descriptor = this.getRepositoryDelegate().getDescriptor(Repository.REP_NAME_DESC); + if (descriptor != null && descriptor.toLowerCase().contains("modeshape")) { + return new ModeShapeMetaData(this); + } + return new JcrMetaData(this); } return metadata; } @@ -459,7 +460,7 @@ * @see java.sql.Connection#createStatement() */ @Override - public Statement createStatement() { + public Statement createStatement() throws SQLException { return new JcrStatement(this); } Index: src/main/java/org/modeshape/jdbc/delegate/LocalRepositoryDelegate.java =================================================================== --- src/main/java/org/modeshape/jdbc/delegate/LocalRepositoryDelegate.java (revision 2478) +++ src/main/java/org/modeshape/jdbc/delegate/LocalRepositoryDelegate.java (working copy) @@ -28,10 +28,14 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.Set; + import javax.jcr.Credentials; +import javax.jcr.LoginException; +import javax.jcr.NoSuchWorkspaceException; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; @@ -43,22 +47,22 @@ import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; + import org.modeshape.jcr.api.Repositories; import org.modeshape.jdbc.JcrDriver; -import org.modeshape.jdbc.JdbcI18n; import org.modeshape.jdbc.JcrDriver.JcrContextFactory; +import org.modeshape.jdbc.JdbcI18n; /** * The LocalRepositoryDelegate provides a local Repository implementation to access the Jcr layer via JNDI Lookup. */ public class LocalRepositoryDelegate extends AbstractRepositoryDelegate { private static final String JNDI_EXAMPLE_URL = JcrDriver.JNDI_URL_PREFIX + "{jndiName}"; + + protected static final Set TRANSACTION_IDS = java.util.Collections.synchronizedSet(new HashSet()); private JcrContextFactory jcrContext = null; - private QueryResult jcrResults; - private Query jcrQuery; - private Session session; - + public LocalRepositoryDelegate( String url, Properties info, JcrContextFactory contextFactory ) { @@ -67,10 +71,7 @@ if (contextFactory == null) { jcrContext = new JcrContextFactory() { public Context createContext( Properties properties ) throws NamingException { - - InitialContext initContext = ((properties == null || properties.isEmpty()) ? new InitialContext() : new InitialContext( - properties)); - + InitialContext initContext = ((properties == null || properties.isEmpty()) ? new InitialContext() : new InitialContext( properties)); return initContext; } }; @@ -85,38 +86,16 @@ return new JNDIConnectionInfo(url, info); } - protected Session session() throws RepositoryException { - if (session == null) { - LOGGER.debug("Setting up session for LocalRepositoryDelegte"); - ConnectionInfo connInfo = getConnectionInfo(); - Repository repository = getRepository(); - Credentials credentials = connInfo.getCredentials(); - String workspaceName = connInfo.getWorkspaceName(); - - if (workspaceName != null) { - this.session = credentials != null ? repository.login(credentials, workspaceName) : repository.login(workspaceName); - LOGGER.trace("Creating session when workspaceName is null"); - } else { - this.session = credentials != null ? repository.login(credentials) : repository.login(); - LOGGER.trace("Creating session for workspace {0}", workspaceName); - } - // this shouldn't happen, but in testing it did occur only because of the repository not being setup correctly - assert session != null; - } - return session; + private JcrContextFactory getJcrContext() { + return jcrContext; } - - @Override - protected boolean isSessionAvailable() { - try { - return (session() != null); - } catch (RepositoryException e) { - } - return false; + + private LocalSession getLocalSession() throws LoginException, NoSuchWorkspaceException, RepositoryException { + return LocalSession.getLocalSessionInstance().getLocalSession(getRepository(), getConnectionInfo()); } - - protected JcrContextFactory getJcrContext() { - return jcrContext; + + private LocalSession getCurrentLocalSession() { + return LocalSession.getLocalSessionInstance().getLocalSession(); } /** @@ -131,13 +110,16 @@ @Override public NodeType nodeType( String name ) throws RepositoryException { - return session().getWorkspace().getNodeTypeManager().getNodeType(name); + LocalSession localSession = getLocalSession(); + return localSession.getSession().getWorkspace().getNodeTypeManager().getNodeType(name); } @Override public List nodeTypes() throws RepositoryException { + + LocalSession localSession = getLocalSession(); List types = new ArrayList(); - NodeTypeIterator its = session().getWorkspace().getNodeTypeManager().getAllNodeTypes(); + NodeTypeIterator its = localSession.getSession().getWorkspace().getNodeTypeManager().getAllNodeTypes(); while (its.hasNext()) { types.add((NodeType)its.next()); } @@ -154,27 +136,11 @@ public QueryResult execute( String query, String language ) throws RepositoryException { LOGGER.trace("Executing query: {0}" + query); - jcrQuery = null; - jcrResults = null; // Create the query ... - Session session = session(); - jcrQuery = session.getWorkspace().getQueryManager().createQuery(query, language); - jcrResults = jcrQuery.execute(); - - // The session just submits the query to it's internal query engine, and this is independent of the - // session's transient state. However, when the results are returned and processed, the session loads - // the nodes into its cache, and therefore this IS dependent upon the session state. If a query is issued - // and the results processed, the session will load each of those results. However, If the content is changed - // and the same (or a similar) query is submitted, the nodes in the results have already been loaded and will - // not be reloaded (or the new ones under these nodes read in). Therefore, we need to refresh the session. - // - // TODO: This is potentially a concurrency issue, because multiple threads may use the same connection and thus - // the same session. But at least this will return the right results. - session.refresh(false); - - return jcrResults;// always a ResultSet - + + final Query jcrQuery = getLocalSession().getSession().getWorkspace().getQueryManager().createQuery(query, language); + return jcrQuery.execute(); } @Override @@ -261,58 +227,56 @@ */ @Override public boolean isValid( final int timeout ) throws RepositoryException { - if (!isSessionAvailable()) return false; - session().getRootNode(); + + LocalSession ls = getLocalSession(); + if (! ls.getSession().isLive()) { + ls.remove(); + return false; + } + return true; } - + /** * {@inheritDoc} - * - * @see java.sql.Connection#commit() + * + * @see org.modeshape.jdbc.delegate.RepositoryDelegate#closeStatement() */ @Override - public void commit() throws RepositoryException { - if (!isSessionAvailable()) return; - Session session = session(); - if (session != null) { - session.save(); - } + public void closeStatement() { + LocalSession session = getCurrentLocalSession(); + try { + if (session != null) { + session.remove(); + } + } catch (Exception e) { + // do nothing + } } - + /** * {@inheritDoc} * - * @see java.sql.Connection#rollback() + * @see java.sql.Connection#close() */ @Override - public void rollback() throws RepositoryException { - if (!isSessionAvailable()) return; - Session session = session(); - if (session != null) { - session.refresh(false); - } + public void close() { + for (Iterator it= TRANSACTION_IDS.iterator(); it.hasNext();) { + LocalSession id = it.next(); + id.remove(); + } + } - } + /** * {@inheritDoc} * - * @see java.sql.Connection#close() + * @see java.sql.Connection#rollback() */ @Override - public void close() { - if (session == null || !isSessionAvailable()) return; - try { - Session session = session(); - if (session != null) { - session.refresh(false); - session.logout(); - } - - } catch (RepositoryException e) { - // do nothing - } + public void rollback() throws RepositoryException { + closeStatement(); } /** @@ -331,7 +295,7 @@ } if (iface.isInstance(Workspace.class)) { - Workspace workspace = session().getWorkspace(); + Workspace workspace = getLocalSession().getSession().getWorkspace(); return iface.cast(workspace); } } catch (RepositoryException re) { @@ -392,5 +356,5 @@ } } } - + } Index: src/main/java/org/modeshape/jdbc/delegate/RepositoryDelegate.java =================================================================== --- src/main/java/org/modeshape/jdbc/delegate/RepositoryDelegate.java (revision 2478) +++ src/main/java/org/modeshape/jdbc/delegate/RepositoryDelegate.java (working copy) @@ -24,8 +24,8 @@ package org.modeshape.jdbc.delegate; import java.sql.Connection; -import java.sql.DatabaseMetaData; import java.sql.SQLException; +import java.sql.Statement; import java.util.List; import java.util.Set; @@ -33,9 +33,6 @@ import javax.jcr.nodetype.NodeType; import javax.jcr.query.QueryResult; -import org.modeshape.jdbc.JcrConnection; - - /** * Represents the communication interface thru which the JDBC logic will obtain a connection and issue commands to the Jcr layer. @@ -49,6 +46,20 @@ */ ConnectionInfo getConnectionInfo(); + /** + * Called when the {@link Statement} the is closed. This enables the underlying connection to the + * JcrRepository remain open until the statement is finished using it. + */ + void closeStatement(); + + /** + * Call to close the delegate connection and any outstanding + * transactions will be closed. + * + * @see java.sql.Connection#close() + */ + void close(); + /** * Call to get {@link NodeType} based on specified name @@ -81,6 +92,8 @@ * Call to create the connection based on the implementation of this interface. * @return Connection * @throws SQLException + * + * @see java.sql.Driver#connect(String, java.util.Properties) */ Connection createConnection() throws SQLException; @@ -112,20 +125,6 @@ */ boolean isValid( int timeout ) throws RepositoryException ; - /** - * Call to close the delegate connection. - * - * @see java.sql.Connection#close() - */ - void close(); - - /** - * Call to create the DatabaseMetaData - * @param connection - * @return JcrMetaData - * @throws RepositoryException - */ - DatabaseMetaData createMetaData(JcrConnection connection ) throws RepositoryException; /** * Index: src/main/java/org/modeshape/jdbc/delegate/HttpRepositoryDelegate.java =================================================================== --- src/main/java/org/modeshape/jdbc/delegate/HttpRepositoryDelegate.java (revision 2478) +++ src/main/java/org/modeshape/jdbc/delegate/HttpRepositoryDelegate.java (working copy) @@ -68,6 +68,7 @@ return new HttpConnectionInfo(url, info); } + @Override public QueryResult execute( String query, String language ) { @@ -175,8 +176,9 @@ this.setRepositoryNames(repositoryNames); } + - /** + /** * @see java.sql.Connection#isValid(int) */ @Override @@ -192,30 +194,11 @@ /** * {@inheritDoc} * - * @see java.sql.Connection#commit() - */ - @Override - public void commit() { - // nothing to do; we can't make any changes - } - - /** - * {@inheritDoc} - * - * @see java.sql.Connection#rollback() - */ - @Override - public void rollback() { - // nothing to do; we can't make any changes - } - - /** - * {@inheritDoc} - * * @see java.sql.Connection#close() */ @Override public void close() { + super.close(); restClient = null; workspace = null; if (nodeTypes != null) nodeTypes.clear(); Index: src/main/java/org/modeshape/jdbc/delegate/LocalSession.java =================================================================== --- src/main/java/org/modeshape/jdbc/delegate/LocalSession.java (revision 0) +++ src/main/java/org/modeshape/jdbc/delegate/LocalSession.java (revision 0) @@ -0,0 +1,104 @@ +/* + * ModeShape (http://www.modeshape.org) + * See the COPYRIGHT.txt file distributed with this work for information + * regarding copyright ownership. Some portions may be licensed + * to Red Hat, Inc. under one or more contributor license agreements. + * See the AUTHORS.txt file in the distribution for a full listing of + * individual contributors. + * + * ModeShape is free software. Unless otherwise indicated, all code in ModeShape + * is licensed to you under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * ModeShape is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.modeshape.jdbc.delegate; + +import javax.jcr.Credentials; +import javax.jcr.LoginException; +import javax.jcr.NoSuchWorkspaceException; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +/** + + */ +public class LocalSession { + + private static LocalSession instance = new LocalSession(); + + public static LocalSession getLocalSessionInstance() { return instance; } + + private ThreadLocal tlocal = new ThreadLocal() { + @Override + protected LocalSession initialValue() { + LocalSession ls = new LocalSession(); + LocalRepositoryDelegate.TRANSACTION_IDS.add(ls); + return ls; + } + }; + + + private Session session; + + private void setSession(Session localSession) { + session = localSession; + } + + public LocalSession getLocalSession() { + return tlocal.get(); + } + + + public LocalSession getLocalSession(final Repository repository, final ConnectionInfo connInfo) throws LoginException, NoSuchWorkspaceException, RepositoryException { + LocalSession lsession = tlocal.get(); + + Credentials credentials = connInfo.getCredentials(); + String workspaceName = connInfo.getWorkspaceName(); + Session session = null; + if (workspaceName != null) { + session = credentials != null ? repository.login(credentials, + workspaceName) : repository.login(workspaceName); + } else { + session = credentials != null ? repository.login(credentials) + : repository.login(); + } + // this shouldn't happen, but in testing it did occur only because of + // the repository not being setup correctly + assert session != null; + + lsession.setSession(session); + + return lsession; + } + + + + public Session getSession() { + return session; + } + + public void remove() { + tlocal.remove(); + LocalRepositoryDelegate.TRANSACTION_IDS.remove(this); + session.logout(); + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer("Session:"); + sb.append(session.toString()); + return sb.toString(); + } + +}; Index: src/main/java/org/modeshape/jdbc/delegate/AbstractRepositoryDelegate.java =================================================================== --- src/main/java/org/modeshape/jdbc/delegate/AbstractRepositoryDelegate.java (revision 2478) +++ src/main/java/org/modeshape/jdbc/delegate/AbstractRepositoryDelegate.java (working copy) @@ -24,17 +24,15 @@ package org.modeshape.jdbc.delegate; import java.sql.Connection; -import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.util.Properties; import java.util.Set; import javax.jcr.Repository; +import javax.jcr.RepositoryException; import org.modeshape.jdbc.JcrConnection; -import org.modeshape.jdbc.JcrMetaData; import org.modeshape.jdbc.JdbcI18n; -import org.modeshape.jdbc.ModeShapeMetaData; import org.modeshape.jdbc.util.Logger; /** @@ -54,11 +52,26 @@ this.url = url; this.propertiesInfo = info; } - - - protected abstract ConnectionInfo createConnectionInfo(final String url, final Properties info); + + /** + * The implementor must return a @link ConnectionInfo that provides the information + * that details + * r + * @param url + * @param info + * @return ConnectionInfo + */ + abstract ConnectionInfo createConnectionInfo(final String url, final Properties info); + + /** + * Implementor is responsible for creating the repository. + * @throws SQLException + */ + abstract void createRepository() throws SQLException; - public synchronized ConnectionInfo getConnectionInfo() { + + @Override + public synchronized ConnectionInfo getConnectionInfo() { if (this.connInfo == null) { this.connInfo = createConnectionInfo(url, propertiesInfo); this.connInfo.init(); @@ -66,13 +79,46 @@ return connInfo; } - protected abstract void createRepository() throws SQLException; + /** + * {@inheritDoc} + * + * @see org.modeshape.jdbc.delegate.RepositoryDelegate#closeStatement() + */ + @Override + public void closeStatement() { + } + + /** + * {@inheritDoc} + * + * @see java.sql.Connection#commit() + */ + @Override + public void commit() throws RepositoryException { + } + + + /** + * {@inheritDoc} + * + * @see java.sql.Connection#close() + */ + @Override + public void close() { + } - protected boolean isSessionAvailable() { - return false; + /** + * {@inheritDoc} + * + * @see java.sql.Connection#rollback() + */ + @Override + public void rollback() throws RepositoryException { } + - public Connection createConnection() throws SQLException { + @Override + public Connection createConnection() throws SQLException { LOGGER.debug("Creating connection for RepositoryDelegte" ); if (this.repository == null) { createRepository(); @@ -129,16 +175,6 @@ return iface.cast(this); } - - - public DatabaseMetaData createMetaData(final JcrConnection connection ) { - - if (getDescriptor(Repository.REP_NAME_DESC) != null) { - if (getDescriptor(Repository.REP_NAME_DESC).toLowerCase().contains("modeshape")) { - return new ModeShapeMetaData(connection); - } - } - return new JcrMetaData(connection); - } - + } + Index: src/main/java/org/modeshape/jdbc/JcrStatement.java =================================================================== --- src/main/java/org/modeshape/jdbc/JcrStatement.java (revision 2478) +++ src/main/java/org/modeshape/jdbc/JcrStatement.java (working copy) @@ -39,7 +39,6 @@ class JcrStatement implements Statement { private final JcrConnection connection; - private QueryResult jcrResults; private ResultSet results; private boolean closed; private SQLWarning warning; @@ -50,9 +49,15 @@ private String sqlLanguage = JcrConnection.JCR_SQL2; - JcrStatement( JcrConnection connection ) { + JcrStatement( JcrConnection connection ) throws SQLException { this.connection = connection; assert this.connection != null; +// try { +// connection.getRepositoryDelegate().startTransaction(); +// } catch (RepositoryException e) { +// e.printStackTrace(); +// throw new SQLException(e.getLocalizedMessage()); +// } } JcrConnection connection() { @@ -242,6 +247,8 @@ @Override public void cancel() throws SQLException { notClosed(); +// connection.getRepositoryDelegate().stopTransaction(); + // Unable to cancel a JCR query ... } @@ -285,7 +292,8 @@ @Override public void close() { if (!closed) { - closed = true; + closed = true; + connection.getRepositoryDelegate().closeStatement(); } } @@ -425,7 +433,7 @@ // Convert the supplied SQL into JCR-SQL2 ... String jcrSql2 = connection.nativeSQL(sql); // Create the query ... - jcrResults = getJcrRepositoryDelegate().execute(jcrSql2, this.sqlLanguage); + final QueryResult jcrResults = getJcrRepositoryDelegate().execute(jcrSql2, this.sqlLanguage); results = new JcrResultSet(this, jcrResults, null); moreResults = 1; } catch (RepositoryException e) { @@ -516,7 +524,7 @@ } if (KEEP_CURRENT_RESULT != current) { // Close (by nulling) the results ... - jcrResults = null; +// jcrResults = null; results = null; } if (moreResults > 0) --moreResults; Index: src/main/java/org/modeshape/jdbc/JcrResultSet.java =================================================================== --- src/main/java/org/modeshape/jdbc/JcrResultSet.java (revision 2478) +++ src/main/java/org/modeshape/jdbc/JcrResultSet.java (working copy) @@ -144,6 +144,7 @@ public void close() { if (!closed) { closed = true; + this.statement.close(); } } Index: src/test/java/org/modeshape/jdbc/JcrStatementTest.java =================================================================== --- src/test/java/org/modeshape/jdbc/JcrStatementTest.java (revision 2478) +++ src/test/java/org/modeshape/jdbc/JcrStatementTest.java (working copy) @@ -36,6 +36,8 @@ import java.sql.Statement; import java.util.List; import java.util.Set; + +import javax.jcr.RepositoryException; import javax.jcr.nodetype.NodeType; import javax.jcr.query.QueryResult; import org.junit.After; @@ -224,7 +226,7 @@ } @Test - public void shouldSupportEquals() { + public void shouldSupportEquals() throws SQLException { assertTrue(stmt.equals(stmt)); JcrStatement stmt2 = null; @@ -233,6 +235,7 @@ assertFalse(stmt.equals(stmt2)); + } finally { if (stmt2 != null) { stmt2.close(); @@ -332,17 +335,21 @@ @Override public void close() { } - + + /** + * {@inheritDoc} + * + * @see org.modeshape.jdbc.delegate.RepositoryDelegate#closeStatement() + */ @Override - public void commit() { + public void closeStatement() { } @Override - public JcrMetaData createMetaData( JcrConnection connection ) { - return null; + public void commit() { } - @Override + @Override public boolean isValid( int timeout ) { return false; } Index: src/test/java/org/modeshape/jdbc/JcrConnectionTest.java =================================================================== --- src/test/java/org/modeshape/jdbc/JcrConnectionTest.java (revision 2478) +++ src/test/java/org/modeshape/jdbc/JcrConnectionTest.java (working copy) @@ -95,8 +95,6 @@ when(session.getRepository()).thenReturn(repository); - - when(jcrDelegate.createMetaData((JcrConnection) conn)).thenReturn(jcrmetadata); } @After