/* * ModeShape (http://www.modeshape.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.modeshape.jcr; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Arrays; import java.util.EnumSet; import org.junit.Before; import org.junit.Test; import org.modeshape.common.FixFor; import org.modeshape.common.collection.Problems; import org.modeshape.jcr.RepositoryConfiguration.AnonymousSecurity; import org.modeshape.jcr.RepositoryConfiguration.Default; import org.modeshape.jcr.RepositoryConfiguration.DocumentOptimization; import org.modeshape.jcr.RepositoryConfiguration.FieldName; import org.modeshape.jcr.RepositoryConfiguration.Indexes; import org.modeshape.jcr.RepositoryConfiguration.JaasSecurity; import org.modeshape.jcr.RepositoryConfiguration.Security; import org.modeshape.jcr.api.index.IndexDefinition; import org.modeshape.jcr.api.index.IndexDefinition.IndexKind; import org.modeshape.jcr.value.binary.MongodbBinaryStore; import org.modeshape.schematic.Schematic; import org.modeshape.schematic.document.Document; import org.modeshape.schematic.document.EditableDocument; import org.modeshape.schematic.document.Editor; public class RepositoryConfigurationTest { private boolean print = false; @Before public void beforeEach() { print = false; } @Test public void shouldSuccessfullyValidateDefaultConfiguration() { assertValid(new RepositoryConfiguration("repoName")); } @Test public void shouldReportErrorWithNoName() { assertNotValid(1, "{}"); } @Test public void shouldReportErrorWithExtraTopLevelProperties() { assertNotValid(1, "{ 'name' = 'nm', 'notValid' : false }"); } @Test public void shouldReportErrorWithExtraStorageProperties() { assertNotValid(1, "{ 'name' = 'nm', 'storage' : { 'notValid' : false } }"); } @Test public void shouldReportErrorWithExtraWorkspacesProperties() { assertNotValid(1, "{ 'name' = 'nm', \"workspaces\" : { \"notValid\" : false } }"); } @Test public void shouldReportErrorWithExtraSecurityProperties() { assertNotValid(1, "{ 'name' = 'nm', \"security\" : { \"notValid\" : false } }"); } @Test public void shouldReportErrorWithExtraQueryProperties() { assertNotValid(1, "{ 'name' = 'nm', \"query\" : { \"notValid\" : false } }"); } @Test public void shouldReportErrorWithExtraSequencingProperties() { assertNotValid(1, "{ 'name' = 'nm', \"sequencing\" : { \"notValid\" : false, 'sequencers' : {} } }"); } @Test public void shouldAcceptSequencerWithNoPathExpression() throws Exception { RepositoryConfiguration config = RepositoryConfiguration.read("{ 'name' : 'Repo', \"sequencing\" : { 'sequencers' : { 'foo' : { 'classname' : 'xsdsequencer' } } } }"); assertValid(config); } @Test public void shouldNotReplaceBlankValuesWithNull() throws Exception { RepositoryConfiguration config = RepositoryConfiguration.read("{ 'name' : 'Repo', 'jndiName' : '' }"); assertThat(config.getJndiName(), is("")); } @Test public void shouldReplaceVariables() { RepositoryConfiguration config = assertValid("{ 'name' = '${os.name} Repository' }"); assertThat(config.getName(), is(System.getProperty("os.name") + " Repository")); System.out.println(config.getDocument()); } @Test public void shouldSuccessfullyValidateSampleRepositoryConfiguration() { assertHasWarnings(0, "sample-repo-config.json"); } @Test public void shouldSuccessfullyValidateSampleRepositoryConfiguration2() { assertValid("config/sample-repo-config.json"); } @Test public void shouldSuccessfullyValidateThoroughRepositoryConfiguration() { assertValid("config/thorough-repo-config.json"); } @Test public void shouldSuccessfullyValidateThoroughRepositoryConfigurationWithDescriptions() { assertValid("config/thorough-with-desc-repo-config.json"); } @Test public void shouldSuccessfullyValidateJndiBasedDataStoreBinaryStorageConfiguration() { assertValid("config/database-jndi-binary-storage.json"); } @Test public void shouldSuccessfullyValidateDriverBasedBinaryStorageConfiguration() { assertValid("config/database-url-binary-storage.json"); } @Test @FixFor( "MODE-2574" ) public void shouldSuccessfullyValidateCassandraBinaryStorageConfiguration() { assertValid("config/cassandra-binary-storage.json"); } @Test @FixFor( {"MODE-2575", "MODE-2635"} ) public void shouldSuccessfullyValidateMongoBinaryStorageConfiguration() throws Exception { RepositoryConfiguration config = assertValid("config/mongo-binary-storage-full-config.json"); Document storageDoc = config.getDocument().getDocument(FieldName.STORAGE).getDocument(FieldName.BINARY_STORAGE); assertEquals(Arrays.asList("192.1.68.1.1:90", "143.22.33.123:120"), storageDoc.get(FieldName.HOST_ADDRESSES)); RepositoryConfiguration.BinaryStorage storage = config.getBinaryStorage(); assertEquals(RepositoryConfiguration.FieldValue.BINARY_STORAGE_TYPE_MONGO, storage.getType()); assertTrue(storage.getBinaryStore() instanceof MongodbBinaryStore); // remove host and port, check that the config is still valid Editor editor = config.edit(); EditableDocument storageDocEditable = editor.getDocument(FieldName.STORAGE).getDocument(FieldName.BINARY_STORAGE); storageDocEditable.remove(FieldName.HOST); storageDocEditable.remove(FieldName.PORT); RepositoryConfiguration configWithoutHostPort = new RepositoryConfiguration(editor.unwrap(), "mongo-config-1"); assertValid(configWithoutHostPort); // remove host addresses as well and check that what remains is not valid storageDocEditable.remove(FieldName.HOST_ADDRESSES); RepositoryConfiguration invalidConfig = new RepositoryConfiguration(editor.unwrap(), "mongo-config-2"); try { invalidConfig.getBinaryStorage().getBinaryStore(); fail("Should not allow a Mongo binary storage without host, port and host addresses"); } catch (IllegalArgumentException e) { //expected } } @Test public void shouldSuccessfullyValidateS3BinaryStorageConfiguration() { assertValid("config/s3-binary-storage.json"); } @Test public void shouldSuccessfullyValidateCompositeBinaryStorageConfiguration() { assertValid("config/composite-binary-storage.json"); } @Test public void shouldSuccessfullyValidateCompositeBinaryStorageWithoutDefaultNamedStoreConfiguration() { assertNotValid(1, "config/composite-binary-storage-without-default.json"); } @Test public void shouldSuccessfullyValidateCustomBinaryStorageConfiguration() { assertValid("config/custom-binary-storage.json"); } @Test public void shouldNotSuccessfullyValidateSampleRepositoryConfigurationWithIndexStorageOnFilesystemAndExtraProperties() { assertNotValid(1, "config/invalid-index-storage-config-filesystem.json"); } @Test public void shouldNotSuccessfullyValidateRepositoryConfigurationWithOldStyleSequencersArray() { assertNotValid(1, "config/invalid-old-style-sequencers-config.json"); } @Test public void shouldSuccessfullyValidateFederationConfiguration() { assertValid("config/repo-config-mock-federation.json"); } @Test public void shouldSuccessfullyValidateFileSystemFederationConfiguration() { assertValid("config/repo-config-filesystem-federation.json"); } @Test public void shouldSuccessfullyValidateConfigurationWithGarbageCollection() { assertValid("config/repo-config-garbage-collection.json"); } @Test public void shouldAlwaysReturnNonNullSecurityComponent() { RepositoryConfiguration config = new RepositoryConfiguration("repoName"); assertThat(config.getSecurity(), is(notNullValue())); } @Test public void shouldNotConfigureJaasByDefault() { RepositoryConfiguration config = new RepositoryConfiguration("repoName"); Security security = config.getSecurity(); JaasSecurity jaas = security.getJaas(); assertThat(jaas, is(nullValue())); } @Test public void shouldHavePolicyByDefaultWhenConfiguringJaas() throws Exception { RepositoryConfiguration config = RepositoryConfiguration.read("{ \"security\" : { \"jaas\" : {} } }"); Security security = config.getSecurity(); JaasSecurity jaas = security.getJaas(); assertThat(jaas, is(notNullValue())); assertThat(jaas.getPolicyName(), is(RepositoryConfiguration.Default.JAAS_POLICY_NAME)); } @Test public void shouldHaveDefinedPolicyWhenConfiguringJaas() throws Exception { RepositoryConfiguration config = RepositoryConfiguration.read("{ \"security\" : { \"jaas\" : { \"policyName\" : \"mypolicy\" } } }"); Security security = config.getSecurity(); JaasSecurity jaas = security.getJaas(); assertThat(jaas, is(notNullValue())); assertThat(jaas.getPolicyName(), is("mypolicy")); } @Test public void shouldConfigureAnonymousByDefault() { RepositoryConfiguration config = new RepositoryConfiguration("repoName"); Security security = config.getSecurity(); AnonymousSecurity anon = security.getAnonymous(); assertThat(anon, is(notNullValue())); assertThat(anon.getAnonymousUsername(), is(RepositoryConfiguration.Default.ANONYMOUS_USERNAME)); assertThat(anon.getAnonymousRoles(), is(RepositoryConfiguration.Default.ANONYMOUS_ROLES)); } @Test public void shouldNotConfigureAnonymousIfNoRolesAreSpecified() throws Exception { RepositoryConfiguration config = RepositoryConfiguration.read("{ \"security\" : { \"anonymous\" : { \"roles\" : [] } } }"); Security security = config.getSecurity(); AnonymousSecurity anon = security.getAnonymous(); assertThat(anon, is(nullValue())); } @Test public void shouldHaveDefinedRolesWhenConfiguringAnonymous() throws Exception { RepositoryConfiguration config = RepositoryConfiguration.read("{ \"security\" : { \"jaas\" : { \"policyName\" : \"mypolicy\" } } }"); Security security = config.getSecurity(); JaasSecurity jaas = security.getJaas(); assertThat(jaas, is(notNullValue())); assertThat(jaas.getPolicyName(), is("mypolicy")); } @Test public void shouldHaveDefinedAnonymousUsernameWhenConfiguringAnonymous() throws Exception { RepositoryConfiguration config = RepositoryConfiguration.read("{ \"security\" : { \"jaas\" : { \"policyName\" : \"mypolicy\" } } }"); Security security = config.getSecurity(); JaasSecurity jaas = security.getJaas(); assertThat(jaas, is(notNullValue())); assertThat(jaas.getPolicyName(), is("mypolicy")); } @FixFor( "MODE-2160" ) @Test public void shouldAlwaysReturnNonNullIndexesComponentForNoIndexes() { RepositoryConfiguration config = new RepositoryConfiguration("repoName"); assertThat(config.getIndexes(), is(notNullValue())); } @FixFor( "MODE-2160" ) @Test public void shouldAlwaysReturnNonNullIndexProvidersList() { RepositoryConfiguration config = new RepositoryConfiguration("repoName"); assertThat(config.getIndexProviders(), is(notNullValue())); } @FixFor( "MODE-2160" ) @Test public void shouldSuccessfullyValidateSampleRepositoryConfigurationWithIndexStorageInRam() { assertValid("config/repo-config-valid-index-providers.json"); } @FixFor( "MODE-2160" ) @Test public void shouldAllowValidRepositoryConfigurationWithIndexProvidersAndNoIndexes() { assertValid("config/repo-config-local-provider-no-indexes.json"); } @FixFor( {"MODE-2160", "MODE-2279"} ) @Test public void shouldAllowValidRepositoryConfigurationWithIndexProvidersAndNotionalIndexes() { RepositoryConfiguration config = assertValid("config/repo-config-local-provider-and-notional-indexes.json"); Indexes indexes = config.getIndexes(); EnumSet<IndexKind> found = EnumSet.noneOf(IndexKind.class); for (String indexName : indexes.getIndexNames()) { IndexDefinition defn = indexes.getIndex(indexName); IndexKind kind = defn.getKind(); found.add(kind); assertThat(kind, is(notNullValue())); } assertThat(found, is(EnumSet.allOf(IndexKind.class))); } @FixFor( {"MODE-2160", "MODE-2279"} ) @Test public void shouldAllowValidRepositoryConfigurationWithIndexProvidersAndIndexes() { RepositoryConfiguration config = assertValid("config/repo-config-local-provider-and-indexes.json"); Indexes indexes = config.getIndexes(); for (String indexName : indexes.getIndexNames()) { IndexDefinition defn = indexes.getIndex(indexName); assertThat(defn.getKind(), is(notNullValue())); } } @FixFor( "MODE-2160" ) @Test public void shouldNotAllowRepositoryConfigurationWithIndexThatRefersToNonExistantIndexProvider() { assertValidWithWarnings(1, "config/invalid-index-with-unmatched-provider.json"); } @FixFor( "MODE-2160" ) @Test public void shouldNotAllowRepositoryConfigurationWithIndexThatHasNoProvider() { assertNotValid(1, "config/invalid-index-with-missing-provider.json"); } @FixFor( "MODE-2160" ) @Test public void shouldNotAllowRepositoryConfigurationWithIndexThatHasMalformedKind() { assertNotValid(1, "config/invalid-index-with-malformed-kind.json"); } @FixFor( "MODE-2160" ) @Test public void shouldNotAllowRepositoryConfigurationWithIndexThatHasMalformedColumns() { assertNotValid(1, "config/invalid-index-with-malformed-columns.json"); } @FixFor( "MODE-2387" ) @Test public void shouldAllowCustomSettingsForLocalIndexProvider() { assertValid("config/local-index-provider-with-custom-settings.json"); } @Test public void shouldAlwaysReturnNonNullSequencingComponent() { RepositoryConfiguration config = new RepositoryConfiguration("repoName"); assertThat(config.getSequencing(), is(notNullValue())); } @Test public void shouldAllowValidButSimpleRepositoryConfiguration() { assertValid("{ \"name\" : \"sample\", \"jndiName\" : \"modeshape_repo1\"}"); } @Test public void shouldAllowValidButSimpleRepositoryConfigurationWithSingleQuotes() { assertValid("{ 'name' : 'sample', 'jndiName' : 'modeshape_repo1'}"); } @Test public void shouldAllowValidProjectionExpressions() throws Exception { assertValid("config/repo-config-federation-projections.json"); } @Test public void shouldNotAllowInvalidProjectionExpressions() throws Exception { assertNotValid(9, "config/repo-config-federation-invalid-projections.json"); } @Test public void shouldAllowJdbcBinaryStorage() throws Exception { assertValid("config/repo-config-jdbc-binary-storage.json"); } @FixFor( "MODE-1988" ) @Test public void shouldNotEnableDocumentOptimizationByDefault() { RepositoryConfiguration config = new RepositoryConfiguration("repoName"); assertThat(config.getDocumentOptimization(), is(notNullValue())); assertThat(config.getDocumentOptimization().isEnabled(), is(false)); } @FixFor( "MODE-1988" ) @Test public void shouldEnableDocumentOptimizationWithEmptyDocumentOptimizationField() { Document doc = Schematic.newDocument(FieldName.NAME, "repoName", FieldName.STORAGE, Schematic.newDocument(FieldName.DOCUMENT_OPTIMIZATION, Schematic.newDocument())); RepositoryConfiguration config = new RepositoryConfiguration(doc, "repoName"); DocumentOptimization opt = config.getDocumentOptimization(); assertThat(opt, is(notNullValue())); assertThat(opt.isEnabled(), is(false)); } @FixFor( "MODE-1988" ) @Test public void shouldEnableDocumentOptimizationWithValidChildCountTargetAndToleranceValues() { Document docOpt = Schematic.newDocument(FieldName.OPTIMIZATION_CHILD_COUNT_TARGET, 500, FieldName.OPTIMIZATION_CHILD_COUNT_TOLERANCE, 10); Document doc = Schematic.newDocument(FieldName.NAME, "repoName", FieldName.STORAGE, Schematic.newDocument(FieldName.DOCUMENT_OPTIMIZATION, docOpt)); RepositoryConfiguration config = new RepositoryConfiguration(doc, "repoName"); DocumentOptimization opt = config.getDocumentOptimization(); assertThat(opt, is(notNullValue())); assertThat(opt.isEnabled(), is(true)); assertThat(opt.getIntervalInHours(), is(Default.OPTIMIZATION_INTERVAL_IN_HOURS)); assertThat(opt.getInitialTimeExpression(), is(Default.OPTIMIZATION_INITIAL_TIME)); assertThat(opt.getThreadPoolName(), is(Default.OPTIMIZATION_POOL)); } @FixFor( "MODE-1988" ) @Test public void shouldDisableDocumentOptimizationWithoutValidChildCountTargetValue() { Document docOpt = Schematic.newDocument(FieldName.OPTIMIZATION_CHILD_COUNT_TOLERANCE, 10); Document doc = Schematic.newDocument(FieldName.NAME, "repoName", FieldName.STORAGE, Schematic.newDocument(FieldName.DOCUMENT_OPTIMIZATION, docOpt)); RepositoryConfiguration config = new RepositoryConfiguration(doc, "repoName"); DocumentOptimization opt = config.getDocumentOptimization(); assertThat(opt, is(notNullValue())); assertThat(opt.isEnabled(), is(false)); } @Test @FixFor( "MODE-1683" ) public void shouldReadJournalingConfiguration() { RepositoryConfiguration configuration = assertValid("config/repo-config-journaling.json"); assertTrue(configuration.getJournaling().isEnabled()); } @Test @FixFor( "MODE-2556" ) public void journalShouldBeDisabledIfConfigurationSectionIsMissing() { Document doc = Schematic.newDocument(FieldName.NAME, "repoName"); RepositoryConfiguration config = new RepositoryConfiguration(doc, "repoName"); assertFalse(config.getJournaling().isEnabled()); } @Test @FixFor( "MODE-2556" ) public void journalShouldBeDisabledIfExplicitlyConfigured() { Document journalingConfig = Schematic.newDocument(FieldName.JOURNAL_ENABLED, false); Document doc = Schematic.newDocument(FieldName.NAME, "repoName", FieldName.JOURNALING, journalingConfig); RepositoryConfiguration config = new RepositoryConfiguration(doc, "repoName"); assertFalse(config.getJournaling().isEnabled()); } @Test public void shouldNotEnableClusteringIfMissingDocument() throws Exception { RepositoryConfiguration config = RepositoryConfiguration.read("{ 'name' = 'nm', 'storage' : {}}"); RepositoryConfiguration.Clustering clusteringConfiguration = config.getClustering(); assertFalse(clusteringConfiguration.isEnabled()); } @Test public void shouldAllowClusteringToBeConfigured() throws Exception { String clusterName = "testCluster"; String channelConfig = "someConfig"; RepositoryConfiguration config = RepositoryConfiguration.read("{ \"clustering\" : {\"clusterName\":\"" + clusterName + "\", \"configuration\": \"" + channelConfig + "\"} }"); RepositoryConfiguration.Clustering clusteringConfiguration = config.getClustering(); assertTrue(clusteringConfiguration.isEnabled()); assertEquals(clusterName, clusteringConfiguration.getClusterName()); assertEquals(channelConfig, clusteringConfiguration.getConfiguration()); } @Test public void shouldUseDefaultClusteringValues() throws Exception { RepositoryConfiguration config = RepositoryConfiguration.read("{ \"clustering\" : {} }"); RepositoryConfiguration.Clustering clusteringConfiguration = config.getClustering(); assertTrue(clusteringConfiguration.isEnabled()); assertEquals(Default.CLUSTER_NAME, clusteringConfiguration.getClusterName()); assertEquals(Default.CLUSTER_CONFIG, clusteringConfiguration.getConfiguration()); } @Test public void shouldUseDefaultLockingTimeout() throws Exception { RepositoryConfiguration config = RepositoryConfiguration.read("{ 'name' = 'nm', 'storage' : {}}"); assertEquals(Default.LOCK_TIMEOUT, config.getLockTimeoutMillis()); } @Test public void shouldUseCustomLockingTimeout() throws Exception { RepositoryConfiguration config = RepositoryConfiguration.read("{ 'name' = 'nm', 'lockTimeoutMillis' : 100}"); assertEquals(100, config.getLockTimeoutMillis()); } protected RepositoryConfiguration assertValid( RepositoryConfiguration config ) { Problems results = config.validate(); assertThat(results.toString(), results.hasProblems(), is(false)); return config; } protected RepositoryConfiguration assertValidWithWarnings( int warnings, RepositoryConfiguration config ) { Problems results = config.validate(); assertThat(results.toString(), results.hasErrors(), is(false)); assertThat(results.toString(), results.warningCount(), is(warnings)); return config; } protected RepositoryConfiguration assertValid( String configContent ) { return assertValid(assertRead(configContent)); } protected RepositoryConfiguration assertValidWithWarnings( int warnings, String configContent ) { return assertValidWithWarnings(warnings, assertRead(configContent)); } protected RepositoryConfiguration assertHasWarnings( int numberOfWarnings, String configContent ) { return assertHasWarnings(numberOfWarnings, assertRead(configContent)); } protected RepositoryConfiguration assertNotValid( int numberOfErrors, RepositoryConfiguration config ) { Problems results = config.validate(); assertThat(results.toString(), results.hasProblems(), is(true)); assertThat(results.toString(), results.hasErrors(), is(true)); assertThat(results.toString(), results.errorCount(), is(numberOfErrors)); if (print) { System.out.println(results); } return config; } protected RepositoryConfiguration assertHasWarnings( int numberOfWarnings, RepositoryConfiguration config ) { Problems results = config.validate(); assertThat(results.toString(), results.warningCount(), is(numberOfWarnings)); assertThat(results.toString(), results.hasWarnings(), is(numberOfWarnings != 0)); if (print) { System.out.println(results); } return config; } protected void print( Object obj ) { if (print) { System.out.println(obj); } } protected RepositoryConfiguration assertNotValid( int numberOfErrors, String configContent ) { return assertNotValid(numberOfErrors, assertRead(configContent)); } protected RepositoryConfiguration assertRead( String content ) { try { return RepositoryConfiguration.read(content); } catch (Exception e) { e.printStackTrace(); fail(e.getMessage()); return null; } } }