Package org.jboss.dna.jcr

Source Code of org.jboss.dna.jcr.JcrRepositoryTest

/*
* JBoss DNA (http://www.jboss.org/dna)
* 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.
*
* JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
* 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.
*
* JBoss DNA 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.jboss.dna.jcr;

import static org.hamcrest.collection.IsArrayContaining.hasItemInArray;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertThat;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.jcr.Credentials;
import javax.jcr.Repository;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.Graph;
import org.jboss.dna.graph.MockSecurityContext;
import org.jboss.dna.graph.Node;
import org.jboss.dna.graph.JaasSecurityContext.UserPasswordCallbackHandler;
import org.jboss.dna.graph.connector.RepositoryConnection;
import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
import org.jboss.dna.graph.connector.RepositorySourceException;
import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
import org.jboss.dna.graph.observe.MockObservable;
import org.jboss.dna.jcr.JcrRepository.Option;
import org.jboss.security.config.IDTrustConfiguration;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

/**
*/
public class JcrRepositoryTest {

    private String sourceName;
    private ExecutionContext context;
    private JcrRepository repository;
    private InMemoryRepositorySource source;
    private Map<String, String> descriptors;
    private RepositoryConnectionFactory connectionFactory;
    private Credentials credentials;
    private Graph sourceGraph;
    private Graph systemGraph;
    private JcrSession session;

    @BeforeClass
    public static void beforeClass() {
        // Initialize IDTrust
        String configFile = "security/jaas.conf.xml";
        IDTrustConfiguration idtrustConfig = new IDTrustConfiguration();

        try {
            idtrustConfig.config(configFile);
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
    }

    @Before
    public void before() throws Exception {
        MockitoAnnotations.initMocks(this);
        sourceName = "repository";

        // Set up the source ...
        source = new InMemoryRepositorySource();
        source.setName(sourceName);

        // Set up the execution context ...
        context = new ExecutionContext();
        credentials = new SimpleCredentials("superuser", "superuser".toCharArray());

        // Stub out the connection factory ...
        connectionFactory = new RepositoryConnectionFactory() {
            /**
             * {@inheritDoc}
             *
             * @see org.jboss.dna.graph.connector.RepositoryConnectionFactory#createConnection(java.lang.String)
             */
            @SuppressWarnings( "synthetic-access" )
            public RepositoryConnection createConnection( String sourceName ) throws RepositorySourceException {
                return sourceName.equals(sourceName) ? source.getConnection() : null;
            }
        };

        // Set up the repository ...
        descriptors = new HashMap<String, String>();
        repository = new JcrRepository(context, connectionFactory, sourceName, new MockObservable(), null, descriptors, null);

        // Set up the graph that goes directly to the source ...
        sourceGraph = Graph.create(source, context);

        // Set up the graph that goes directly to the system source ...
        systemGraph = repository.createSystemGraph(context);
    }

    @After
    public void afterEach() {
        if (session != null) {
            try {
                session.logout();
            } finally {
                session = null;
            }
        }
    }

    @Test
    public void shouldFailIfWorkspacesSharingSystemBranchConstantIsFalse() {
        // Check that the debugging flag is ALWAYS set to true...
        assertThat(JcrRepository.WORKSPACES_SHARE_SYSTEM_BRANCH, is(true));
    }

    @Test
    public void shouldAllowNullDescriptors() throws Exception {
        new JcrRepository(context, connectionFactory, sourceName, new MockObservable(), null, null, null);
    }

    @Test( expected = IllegalArgumentException.class )
    public void shouldNotAllowNullExecutionContext() throws Exception {
        new JcrRepository(null, connectionFactory, sourceName, new MockObservable(), null, descriptors, null);
    }

    @Test( expected = IllegalArgumentException.class )
    public void shouldNotAllowNullConnectionFactories() throws Exception {
        new JcrRepository(context, null, sourceName, new MockObservable(), null, descriptors, null);
    }

    @Test( expected = IllegalArgumentException.class )
    public void shouldNotAllowNullObservable() throws Exception {
        new JcrRepository(context, connectionFactory, sourceName, null, null, null, null);
    }

    @Test( expected = IllegalArgumentException.class )
    public void shouldNotAllowNullSourceName() throws Exception {
        new JcrRepository(context, connectionFactory, null, new MockObservable(), null, descriptors, null);
    }

    @Test( expected = IllegalArgumentException.class )
    public void shouldNotAllowNoDescriptorKey() {
        repository.getDescriptor(null);
    }

    @Test( expected = IllegalArgumentException.class )
    public void shouldNotAllowEmptyDescriptorKey() {
        repository.getDescriptor("");
    }

    @Test
    public void shouldProvideBuiltInDescriptorKeys() {
        testDescriptorKeys(repository);
    }

    @Test
    public void shouldProvideDescriptorValues() {
        testDescriptorValues(repository);
    }

    @Test
    public void shouldProvideBuiltInDescriptorsWhenNotSuppliedDescriptors() throws Exception {
        Repository repository = new JcrRepository(context, connectionFactory, sourceName, new MockObservable(), null,
                                                  descriptors, null);
        testDescriptorKeys(repository);
        testDescriptorValues(repository);
    }

    @Test
    public void shouldProvideObserver() {
        assertThat(this.repository.getObserver(), is(notNullValue()));
    }

    @Test
    public void shouldProvideRepositoryObservable() {
        assertThat(this.repository.getRepositoryObservable(), is(notNullValue()));
    }

    @Test
    public void shouldHaveDefaultOptionsWhenNotOverridden() throws Exception {
        JcrRepository repository = new JcrRepository(context, connectionFactory, sourceName, new MockObservable(), null,
                                                     descriptors, null);
        assertThat(repository.getOptions().get(JcrRepository.Option.PROJECT_NODE_TYPES),
                   is(JcrRepository.DefaultOption.PROJECT_NODE_TYPES));
    }

    @Test
    public void shouldProvideUserSuppliedDescriptors() throws Exception {
        Map<String, String> descriptors = new HashMap<String, String>();
        descriptors.put("property", "value");
        Repository repository = new JcrRepository(context, connectionFactory, sourceName, new MockObservable(), null,
                                                  descriptors, null);
        testDescriptorKeys(repository);
        testDescriptorValues(repository);
        assertThat(repository.getDescriptor("property"), is("value"));
    }

    @Test( expected = javax.jcr.LoginException.class )
    public void shouldNotAllowLoginWithNoCredentials() throws Exception {
        // This would work iff this code was executing in a privileged block, but it's not
        repository.login();
    }

    @SuppressWarnings( "cast" )
    @Test
    public void shouldAllowLoginWithNoCredentialsInPrivilegedBlock() throws Exception {
        LoginContext login = new LoginContext("dna-jcr", new UserPasswordCallbackHandler("superuser", "superuser".toCharArray()));
        login.login();

        Subject subject = login.getSubject();

        Session session = (Session)Subject.doAsPrivileged(subject, new PrivilegedExceptionAction<Session>() {

            @SuppressWarnings( "synthetic-access" )
            public Session run() throws Exception {
                return repository.login();
            }

        }, AccessController.getContext());

        assertThat(session, is(notNullValue()));
        assertThat(session.getUserID(), is("superuser"));
        login.logout();
    }

    @Test
    public void shouldAllowLoginWithNoCredentialsIfAnonAccessEnabled() throws Exception {
        Map<JcrRepository.Option, String> options = new HashMap<JcrRepository.Option, String>();
        options.put(JcrRepository.Option.ANONYMOUS_USER_ROLES, JcrSession.DNA_READ_PERMISSION);
        JcrRepository repository = new JcrRepository(context, connectionFactory, sourceName, new MockObservable(), null,
                                                     descriptors, options);

        session = (JcrSession)repository.login();

        assertThat(session, is(notNullValue()));
        assertThat(session.getUserID(), is(JcrRepository.ANONYMOUS_USER_NAME));

    }

    @Test
    public void shouldAllowLoginWithProperCredentials() throws Exception {
        repository.login(credentials);
        repository.login(new SecurityContextCredentials(
                                                        new MockSecurityContext(
                                                                                null,
                                                                                Collections.singleton(JcrSession.DNA_ADMIN_PERMISSION))));
    }

    @Test
    public void shouldAllowLoginWithNoWorkspaceName() throws Exception {
        Session session = repository.login(credentials, null);
        assertThat(session, notNullValue());
        session.logout();
        session = repository.login(new SecurityContextCredentials(
                                                                  new MockSecurityContext(
                                                                                          null,
                                                                                          Collections.singleton(JcrSession.DNA_ADMIN_PERMISSION))),
                                   (String)null);
        assertThat(session, notNullValue());
        session.logout();
    }

    @Test( expected = IllegalArgumentException.class )
    public void shouldNotAllowLoginIfCredentialsDoNotProvideJaasMethod() throws Exception {
        repository.login(Mockito.mock(Credentials.class));
    }

    @Test( expected = IllegalArgumentException.class )
    public void shouldNotAllowLoginIfCredentialsReturnNoAccessControlContext() throws Exception {
        repository.login(new Credentials() {

            private static final long serialVersionUID = 1L;

            @SuppressWarnings( "unused" )
            public AccessControlContext getAccessControlContext() {
                return null;
            }
        });
    }

    @Test( expected = IllegalArgumentException.class )
    public void shouldNotAllowLoginIfCredentialsReturnNoLoginContext() throws Exception {
        repository.login(new Credentials() {

            private static final long serialVersionUID = 1L;

            @SuppressWarnings( "unused" )
            public LoginContext getLoginContext() {
                return null;
            }
        });
    }

    @Test
    public void shouldHaveRootNode() throws Exception {
        session = createSession();
        javax.jcr.Node root = session.getRootNode();
        String uuid = root.getUUID();

        // Get the root via the direct graph ...
        Node dnaRoot = sourceGraph.getNodeAt("/");
        UUID dnaRootUuid = dnaRoot.getLocation().getUuid();

        // They should have the same UUID ...
        assertThat(uuid, is(dnaRootUuid.toString()));

        // Get the children of the root node ...
        javax.jcr.NodeIterator iter = root.getNodes();
        javax.jcr.Node system = iter.nextNode();
        assertThat(system.getName(), is("jcr:system"));

        // Add a child node ...
        javax.jcr.Node childA = root.addNode("childA", "nt:unstructured");
        assertThat(childA, is(notNullValue()));
        iter = root.getNodes();
        javax.jcr.Node system2 = iter.nextNode();
        javax.jcr.Node childA2 = iter.nextNode();
        assertThat(system2.getName(), is("jcr:system"));
        assertThat(childA2.getName(), is("childA"));
    }

    @Test
    public void shouldHaveSystemBranch() throws Exception {
        session = createSession();
        javax.jcr.Node root = session.getRootNode();
        AbstractJcrNode system = (AbstractJcrNode)root.getNode("jcr:system");
        UUID uuid = system.location.getUuid();

        for (int i = 0; i != 3; ++i) {
            // Get the same node via the direct graph ...
            Node dnaSystem = systemGraph.getNodeAt("/jcr:system");
            UUID dnaSystemUuid = dnaSystem.getLocation().getUuid();

            // They should have the same UUID ...
            assertThat(uuid, is(dnaSystemUuid));
        }
    }

    @Test
    public void shouldHaveRegisteredThoseNamespacesNeedeByDna() throws Exception {
        session = createSession();
        // Don't use the constants, since this needs to check that the actual values are correct
        assertThat(session.getNamespaceURI("dna"), is("http://www.jboss.org/dna/1.0"));
        assertThat(session.getNamespaceURI("dnaint"), is("http://www.jboss.org/dna/internal/1.0"));
    }

    @Test
    public void shouldHaveRegisteredThoseNamespacesDefinedByTheJcrSpecification() throws Exception {
        session = createSession();
        // Don't use the constants, since this needs to check that the actual values are correct
        assertThat(session.getNamespaceURI("dna"), is("http://www.jboss.org/dna/1.0"));
        assertThat(session.getNamespaceURI("jcr"), is("http://www.jcp.org/jcr/1.0"));
        assertThat(session.getNamespaceURI("mix"), is("http://www.jcp.org/jcr/mix/1.0"));
        assertThat(session.getNamespaceURI("nt"), is("http://www.jcp.org/jcr/nt/1.0"));
        assertThat(session.getNamespaceURI(""), is(""));
    }

    @Test
    public void shouldHaveRegisteredThoseNamespacesDefinedByTheJcrApiJavaDoc() throws Exception {
        session = createSession();
        // Don't use the constants, since this needs to check that the actual values are correct
        assertThat(session.getNamespaceURI("sv"), is("http://www.jcp.org/jcr/sv/1.0"));
        assertThat(session.getNamespaceURI("xmlns"), is("http://www.w3.org/2000/xmlns/"));
    }

    @Test
    public void shouldParseSourceNameOptionWithOnlySourceName() {
        assertSourceWorkspacePair("source name", "source name", null);
        assertSourceWorkspacePair(" \t source name \n ", "source name", null);
        assertSourceWorkspacePair(" \t source \\@ name \n ", "source @ name", null);
    }

    @Test
    public void shouldParseSourceNameOptionWithWorkspaceNameAndSourceName() {
        assertSourceWorkspacePair("workspace@source", "source", "workspace");
        assertSourceWorkspacePair(" \t workspace\t@ \t source \t \n", "source", "workspace");
        assertSourceWorkspacePair(" \t workspace\\@ name \t@ \t source\\@name \t \n", "source@name", "workspace@ name");
        assertSourceWorkspacePair("@ \t source \\@ name \n ", "source @ name", "");
        assertSourceWorkspacePair("   @ \t source \\@ name \n ", "source @ name", "");
    }

    @Test( expected = IllegalArgumentException.class )
    public void shouldFailToParseSourceNameOptionThatHasZeroLengthSource() {
        new JcrRepository.SourceWorkspacePair("workspace@");
    }

    @Test( expected = IllegalArgumentException.class )
    public void shouldFailToParseSourceNameOptionThatHasBlankSource() {
        new JcrRepository.SourceWorkspacePair("workspace@  ");
    }

    @Test
    public void shouldParseSourceNameOptionThatHasBlankSourceAndWorkspace() {
        assertSourceWorkspacePair("@", "", "");
        assertSourceWorkspacePair(" @ ", "", "");
    }

    protected void assertSourceWorkspacePair( String value,
                                              String expectedSourceName,
                                              String expectedWorkspaceName ) {
        JcrRepository.SourceWorkspacePair pair = new JcrRepository.SourceWorkspacePair(value);
        assertThat(pair, is(notNullValue()));
        assertThat(pair.getSourceName(), is(expectedSourceName));
        assertThat(pair.getWorkspaceName(), is(expectedWorkspaceName));
    }

    protected JcrSession createSession() throws Exception {
        LoginContext login = new LoginContext("dna-jcr", new UserPasswordCallbackHandler("superuser", "superuser".toCharArray()));
        login.login();

        Subject subject = login.getSubject();
        JcrSession session = (JcrSession)Subject.doAsPrivileged(subject, new PrivilegedExceptionAction<Session>() {

            @SuppressWarnings( "synthetic-access" )
            public Session run() throws Exception {
                return repository.login();
            }

        }, AccessController.getContext());
        return session;
    }

    private void testDescriptorKeys( Repository repository ) {
        String[] keys = repository.getDescriptorKeys();
        assertThat(keys, notNullValue());
        assertThat(keys.length >= 15, is(true));
        assertThat(keys, hasItemInArray(Repository.LEVEL_1_SUPPORTED));
        assertThat(keys, hasItemInArray(Repository.LEVEL_2_SUPPORTED));
        assertThat(keys, hasItemInArray(Repository.OPTION_LOCKING_SUPPORTED));
        assertThat(keys, hasItemInArray(Repository.OPTION_OBSERVATION_SUPPORTED));
        assertThat(keys, hasItemInArray(Repository.OPTION_QUERY_SQL_SUPPORTED));
        assertThat(keys, hasItemInArray(Repository.OPTION_TRANSACTIONS_SUPPORTED));
        assertThat(keys, hasItemInArray(Repository.OPTION_VERSIONING_SUPPORTED));
        assertThat(keys, hasItemInArray(Repository.QUERY_XPATH_DOC_ORDER));
        assertThat(keys, hasItemInArray(Repository.QUERY_XPATH_POS_INDEX));
        assertThat(keys, hasItemInArray(Repository.REP_NAME_DESC));
        assertThat(keys, hasItemInArray(Repository.REP_VENDOR_DESC));
        assertThat(keys, hasItemInArray(Repository.REP_VENDOR_URL_DESC));
        assertThat(keys, hasItemInArray(Repository.REP_VERSION_DESC));
        assertThat(keys, hasItemInArray(Repository.SPEC_NAME_DESC));
        assertThat(keys, hasItemInArray(Repository.SPEC_VERSION_DESC));
    }

    private void testDescriptorValues( Repository repository ) {
        assertThat(repository.getDescriptor(Repository.LEVEL_1_SUPPORTED), is("true"));
        assertThat(repository.getDescriptor(Repository.LEVEL_2_SUPPORTED), is("true"));
        assertThat(repository.getDescriptor(Repository.OPTION_LOCKING_SUPPORTED), is("true"));
        assertThat(repository.getDescriptor(Repository.OPTION_OBSERVATION_SUPPORTED), is("true"));
        assertThat(repository.getDescriptor(Repository.OPTION_QUERY_SQL_SUPPORTED), is("false"));
        assertThat(repository.getDescriptor(Repository.OPTION_TRANSACTIONS_SUPPORTED), is("false"));
        assertThat(repository.getDescriptor(Repository.OPTION_VERSIONING_SUPPORTED), is("false"));
        assertThat(repository.getDescriptor(Repository.QUERY_XPATH_DOC_ORDER), is("true"));
        assertThat(repository.getDescriptor(Repository.QUERY_XPATH_POS_INDEX), is("true"));
        assertThat(repository.getDescriptor(Repository.REP_NAME_DESC), is(JcrI18n.REP_NAME_DESC.text()));
        assertThat(repository.getDescriptor(Repository.REP_VENDOR_DESC), is(JcrI18n.REP_VENDOR_DESC.text()));
        assertThat(repository.getDescriptor(Repository.REP_VENDOR_URL_DESC), is("http://www.jboss.org/dna"));
        assertThat(repository.getDescriptor(Repository.REP_VERSION_DESC), is("0.4"));
        assertThat(repository.getDescriptor(Repository.SPEC_NAME_DESC), is(JcrI18n.SPEC_NAME_DESC.text()));
        assertThat(repository.getDescriptor(Repository.SPEC_VERSION_DESC), is("1.0"));
    }

    @Ignore( "GC behavior is non-deterministic from the application's POV - this test _will_ occasionally fail" )
    @Test
    public void shouldAllowManySessionLoginsAndLogouts() throws Exception {
        // Use a different repository that supports anonymous logins to make this test cleaner
        Map<Option, String> options = new HashMap<Option, String>();
        options.put(JcrRepository.Option.ANONYMOUS_USER_ROLES, JcrSession.DNA_ADMIN_PERMISSION);
        JcrRepository repository = new JcrRepository(context, connectionFactory, sourceName, new MockObservable(), null,
                                                     descriptors, options);

        Session session;

        for (int i = 0; i < 10000; i++) {
            session = repository.login();
            session.logout();
        }

        session = repository.login();
        session = null;

        // Give the gc a chance to run
        System.gc();
        Thread.sleep(100);

        assertThat(repository.activeSessions().size(), is(0));
    }

    @Ignore( "This test normally sleeps for 30 seconds" )
    @Test
    public void shouldCleanUpLocksFromDeadSessions() throws Exception {
        // Use a different repository that supports anonymous logins to make this test cleaner
        Map<Option, String> options = new HashMap<Option, String>();
        options.put(JcrRepository.Option.ANONYMOUS_USER_ROLES, JcrSession.DNA_ADMIN_PERMISSION);
        JcrRepository repository = new JcrRepository(context, connectionFactory, sourceName, new MockObservable(), null,
                                                     descriptors, options);

        String lockedNodeName = "lockedNode";
        JcrSession locker = (JcrSession)repository.login();

        // Create a node to lock
        javax.jcr.Node lockedNode = locker.getRootNode().addNode(lockedNodeName);
        lockedNode.addMixin("mix:lockable");
        locker.save();

        // Create a session-scoped lock (not deep)
        lockedNode.lock(false, true);
        assertThat(lockedNode.isLocked(), is(true));

        Session reader = repository.login();
        javax.jcr.Node readerNode = (javax.jcr.Node)reader.getItem("/" + lockedNodeName);
        assertThat(readerNode.isLocked(), is(true));

        // No locks should have changed yet.
        repository.cleanUpLocks();
        assertThat(lockedNode.isLocked(), is(true));
        assertThat(readerNode.isLocked(), is(true));

        /*      
         * Simulate the GC cleaning up the session and it being purged from the activeSessions() map.
         * This can't really be tested in a consistent way due to a lack of specificity around when
         * the garbage collector runs. The @Ignored test above does cause a GC sweep on by computer and
         * confirms that the code works in principle. A different chicken dance may be required to
         * fully test this on a different computer.
         */
        repository.activeSessions.remove(locker);
        Thread.sleep(JcrEngine.LOCK_EXTENSION_INTERVAL_IN_MILLIS + 100);

        // The locker thread should be inactive and the lock cleaned up
        repository.cleanUpLocks();
        assertThat(readerNode.isLocked(), is(false));
    }

}
TOP

Related Classes of org.jboss.dna.jcr.JcrRepositoryTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.