Index: extensions/modeshape-connector-store-jpa/src/main/java/org/modeshape/connector/store/jpa/model/simple/SimpleJpaRepository.java =================================================================== --- extensions/modeshape-connector-store-jpa/src/main/java/org/modeshape/connector/store/jpa/model/simple/SimpleJpaRepository.java (revision 2602) +++ extensions/modeshape-connector-store-jpa/src/main/java/org/modeshape/connector/store/jpa/model/simple/SimpleJpaRepository.java (working copy) @@ -226,7 +226,8 @@ public class SimpleJpaRepository extends MapRepository { */ @Override public Set getWorkspaceNames() { - Set workspaceNames = new HashSet(super.getWorkspaceNames()); + // Have to look this up in the database, in case new workspaces were added by another connection ... + Set workspaceNames = new HashSet(workspaceEntities.getWorkspaceNames()); workspaceNames.addAll(predefinedWorkspaceNames); return workspaceNames; Index: modeshape-integration-tests/src/test/java/org/modeshape/test/integration/jpa/JcrRepositoryWithJpaSourceTest.java =================================================================== --- modeshape-integration-tests/src/test/java/org/modeshape/test/integration/jpa/JcrRepositoryWithJpaSourceTest.java (revision 2602) +++ modeshape-integration-tests/src/test/java/org/modeshape/test/integration/jpa/JcrRepositoryWithJpaSourceTest.java (working copy) @@ -31,6 +31,7 @@ import javax.jcr.Node; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; +import javax.jcr.Workspace; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -123,4 +124,44 @@ public class JcrRepositoryWithJpaSourceTest { assertThat(root.hasNode(pathToNode), is(false)); } + @Test + public void shouldAllowCreatingWorkspaces() throws Exception { + String workspaceName = "MyNewWorkspace"; + + // Create a session ... + Session jcrSession = repository.login(); + Workspace defaultWorkspace = jcrSession.getWorkspace(); + defaultWorkspace.createWorkspace(workspaceName); + assertAccessibleWorkspace(defaultWorkspace, workspaceName); + jcrSession.logout(); + + Session session2 = repository.login(workspaceName); + session2.logout(); + } + + protected void assertAccessibleWorkspace( Session session, + String workspaceName ) throws Exception { + assertAccessibleWorkspace(session.getWorkspace(), workspaceName); + } + + protected void assertAccessibleWorkspace( Workspace workspace, + String workspaceName ) throws Exception { + assertContains(workspace.getAccessibleWorkspaceNames(), workspaceName); + } + + protected void assertContains( String[] actuals, + String... expected ) { + // Each expected must appear in the actuals ... + for (String expect : expected) { + if (expect == null) continue; + boolean found = false; + for (String actual : actuals) { + if (expect.equals(actual)) { + found = true; + break; + } + } + assertThat("Did not find '" + expect + "' in the actuals: " + actuals, found, is(true)); + } + } } Index: modeshape-jcr/src/main/java/org/modeshape/jcr/JcrRepository.java =================================================================== --- modeshape-jcr/src/main/java/org/modeshape/jcr/JcrRepository.java (revision 2602) +++ modeshape-jcr/src/main/java/org/modeshape/jcr/JcrRepository.java (working copy) @@ -478,6 +478,7 @@ public class JcrRepository implements Repository { private final RepositoryObservationManager repositoryObservationManager; private final SecurityContext anonymousUserContext; private final QueryParsers queryParsers; + private Set cachedWorkspaceNames = new HashSet(); // Until the federated connector supports queries, we have to use a search engine ... private final RepositoryQueryManager queryManager; @@ -827,7 +828,8 @@ public class JcrRepository implements Repository { ValueFactories factories = this.getExecutionContext().getValueFactories(); List values = new LinkedList(); - for (String name : workspaceNames()) { + this.cachedWorkspaceNames = readWorkspaceNamesFromSource(); + for (String name : this.cachedWorkspaceNames) { values.add(new JcrValue(factories, null, PropertyType.STRING, name)); } descriptors.put(Repository.REPOSITORY_WORKSPACES, values.toArray(new JcrValue[values.size()])); @@ -891,8 +893,7 @@ public class JcrRepository implements Repository { graph.merge(initialContentGraph); // uses its own batch } String actualName = graphWorkspace.getName(); - addWorkspace(actualName, false); - updateWorkspaceNames(); + addWorkspace(actualName, false); // this updates the workspace names } /** @@ -1405,6 +1406,13 @@ public class JcrRepository implements Repository { * @return a list of all workspace names, without regard to the access permissions of any particular user */ Set workspaceNames() { + return cachedWorkspaceNames; + } + + /** + * @return a list of all workspace names, without regard to the access permissions of any particular user + */ + private Set readWorkspaceNamesFromSource() { return Graph.create(sourceName, connectionFactory, executionContext).getWorkspaces(); } Index: modeshape-jcr/src/test/java/org/modeshape/jcr/JcrEngineTest.java =================================================================== --- modeshape-jcr/src/test/java/org/modeshape/jcr/JcrEngineTest.java (revision 2602) +++ modeshape-jcr/src/test/java/org/modeshape/jcr/JcrEngineTest.java (working copy) @@ -28,6 +28,7 @@ import static org.hamcrest.core.IsNull.notNullValue; import static org.junit.Assert.assertThat; import java.net.URL; import javax.jcr.Session; +import javax.jcr.Workspace; import javax.jcr.nodetype.NodeType; import org.junit.After; import org.junit.Test; @@ -209,4 +210,46 @@ public class JcrEngineTest { } + @Test + public void shouldAllowCreatingWorkspaces() throws Exception { + configuration = new JcrConfiguration().loadFrom("src/test/resources/config/configRepositoryWithDefaultNamespace.xml"); + engine = configuration.build(); + engine.start(); + repository = engine.getRepository("mode:Car Repository"); + + String workspaceName = "MyNewWorkspace"; + + // Create a session ... + Session jcrSession = repository.login(); + Workspace defaultWorkspace = jcrSession.getWorkspace(); + defaultWorkspace.createWorkspace(workspaceName); + assertAccessibleWorkspace(defaultWorkspace, workspaceName); + jcrSession.logout(); + } + + protected void assertAccessibleWorkspace( Session session, + String workspaceName ) throws Exception { + assertAccessibleWorkspace(session.getWorkspace(), workspaceName); + } + + protected void assertAccessibleWorkspace( Workspace workspace, + String workspaceName ) throws Exception { + assertContains(workspace.getAccessibleWorkspaceNames(), workspaceName); + } + + protected void assertContains( String[] actuals, + String... expected ) { + // Each expected must appear in the actuals ... + for (String expect : expected) { + if (expect == null) continue; + boolean found = false; + for (String actual : actuals) { + if (expect.equals(actual)) { + found = true; + break; + } + } + assertThat("Did not find '" + expect + "' in the actuals: " + actuals, found, is(true)); + } + } } Index: modeshape-jcr/src/test/java/org/modeshape/jcr/JcrRepositoryTest.java =================================================================== --- modeshape-jcr/src/test/java/org/modeshape/jcr/JcrRepositoryTest.java (revision 2602) +++ modeshape-jcr/src/test/java/org/modeshape/jcr/JcrRepositoryTest.java (working copy) @@ -713,4 +713,64 @@ public class JcrRepositoryTest { assertThat(g37, is(notNullValue())); } + @Test + public void shouldAllowCreatingWorkspaces() throws Exception { + + // Create the JcrRepositoyr instance ... + assertThat(source.getCapabilities().supportsCreatingWorkspaces(), is(true)); + descriptors.put(Repository.OPTION_WORKSPACE_MANAGEMENT_SUPPORTED, "true"); + repository = new JcrRepository(context, connectionFactory, sourceName, new MockObservable(), source.getCapabilities(), + descriptors, null, null); + + // Create several sessions ... + Session session2 = null; + Session session3 = null; + try { + session = createSession(); + session2 = createSession(); + + // Create a new workspace ... + String newWorkspaceName = "MyCarWorkspace"; + session.getWorkspace().createWorkspace(newWorkspaceName); + assertAccessibleWorkspace(session, newWorkspaceName); + assertAccessibleWorkspace(session2, newWorkspaceName); + session.logout(); + + session3 = createSession(); + assertAccessibleWorkspace(session2, newWorkspaceName); + assertAccessibleWorkspace(session3, newWorkspaceName); + + // Create a session for this new workspace ... + session = createSession(newWorkspaceName); + } finally { + try { + if (session2 != null) session2.logout(); + } finally { + if (session3 != null) session3.logout(); + } + } + + } + + protected void assertAccessibleWorkspace( Session session, + String workspaceName ) throws Exception { + assertContains(session.getWorkspace().getAccessibleWorkspaceNames(), workspaceName); + } + + protected void assertContains( String[] actuals, + String... expected ) { + // Each expected must appear in the actuals ... + for (String expect : expected) { + if (expect == null) continue; + boolean found = false; + for (String actual : actuals) { + if (expect.equals(actual)) { + found = true; + break; + } + } + assertThat("Did not find '" + expect + "' in the actuals: " + actuals, found, is(true)); + } + } + }