/*
* 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.jboss.subsystem;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CHILD_TYPE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUCCESS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.modeshape.jboss.subsystem.ModeShapeExtension.SUBSYSTEM_NAME;
import static org.modeshape.jboss.subsystem.ModelKeys.REPOSITORY;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.subsystem.test.AbstractSubsystemBaseTest;
import org.jboss.as.subsystem.test.KernelServices;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
import org.junit.Assert;
import org.junit.Test;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* Unit test for the ModeShape AS subsystem.
*/
@SuppressWarnings( "nls" )
public class ModeShapeConfigurationTest extends AbstractSubsystemBaseTest {
public ModeShapeConfigurationTest() {
super(SUBSYSTEM_NAME, new ModeShapeExtension());
}
@Override
protected String getSubsystemXml() throws IOException {
return readResource("modeshape-sample-config.xml");
}
@Override
protected String getSubsystemXml( String configId ) throws IOException {
if (configId == null) {
return getSubsystemXml();
}
String config = "modeshape-" + configId + "-config.xml";
if (ModeShapeConfigurationTest.class.getResource(config) != null) {
return readResource(config);
}
return readResource(configId);
}
@Test
public void testMinimalConfiguration() throws Exception {
standardSubsystemTest("minimal");
}
@Test
public void testFullConfiguration() throws Exception {
standardSubsystemTest("full");
}
@Test
public void testConfigurationWithIndexStorage() throws Exception {
standardSubsystemTest("modeshape-index-storage.xml", false);
}
@Test
public void testConfigurationWithAllIndexTypes() throws Exception {
// fix for MODE-2348
standardSubsystemTest("modeshape-index-types.xml");
}
@Test
public void testConfigurationWithFileBinaryStorage() throws Exception {
standardSubsystemTest("modeshape-file-binary-storage.xml", false);
}
@Test
public void testConfigurationWithTransientBinaryStorage() throws Exception {
standardSubsystemTest("modeshape-transient-binary-storage.xml");
}
@Test
public void testConfigurationWithDBBinaryStorage() throws Exception {
standardSubsystemTest("modeshape-db-binary-storage.xml");
}
@Test
public void testConfigurationWithCassandraBinaryStorage() throws Exception {
standardSubsystemTest("modeshape-cassandra-binary-storage.xml");
}
@Test
public void testConfigurationWithMongoBinaryStorage() throws Exception {
standardSubsystemTest("modeshape-mongo-binary-storage.xml");
}
@Test
public void testConfigurationWithS3BinaryStorage() throws Exception {
standardSubsystemTest("modeshape-s3-binary-storage.xml");
}
@Test
public void testConfigurationWithCustomBinaryStorage() throws Exception {
standardSubsystemTest("modeshape-custom-binary-storage.xml");
}
@Test(expected = XMLStreamException.class)
public void shouldValidateFileBinaryStoreAttributesAgainstSchema() throws Exception {
standardSubsystemTest("modeshape-invalid-file-binary-storage.xml");
}
@Test
public void testConfigurationWithCompositeBinaryStores() throws Exception {
standardSubsystemTest("modeshape-composite-binary-storage-config.xml", false);
}
@Test( expected = XMLStreamException.class )
public void shouldRejectInvalidCompositeBinaryStoreConfiguration() throws Exception {
standardSubsystemTest("modeshape-invalid-composite-binary-storage.xml");
}
@Test
public void testConfigurationWithWorkspaceInitialContent() throws Exception {
standardSubsystemTest("modeshape-initial-content-config.xml", false);
}
@Test
public void testConfigurationWithClustering() throws Exception {
standardSubsystemTest("modeshape-clustered-config.xml");
}
@Test
public void testConfigurationWithNodeTypes() throws Exception {
standardSubsystemTest("modeshape-node-types-config.xml");
}
@Test
public void testConfigurationWithCustomAuthenticators() throws Exception {
standardSubsystemTest("modeshape-custom-authenticators-config.xml", false);
}
@Test
public void testConfigurationWithWorkspacesCacheContainer() throws Exception {
standardSubsystemTest("modeshape-workspaces-cache-config.xml", false);
}
@Test
public void testConfigurationWithExternalSources() throws Exception {
standardSubsystemTest("modeshape-federation-config.xml", false);
}
@Test
public void testConfigurationWithGarbageCollectionSpecified() throws Exception {
standardSubsystemTest("modeshape-garbage-collection.xml", false);
}
@Test
public void testConfigurationWithWebapps() throws Exception {
standardSubsystemTest("modeshape-webapp-config.xml", false);
}
@Test
public void testConfigurationWithJournaling() throws Exception {
standardSubsystemTest("modeshape-journaling.xml");
}
@Test
public void testConfigurationWithOptimization() throws Exception {
standardSubsystemTest("modeshape-optimiziation-config.xml", false);
}
@Test
public void testConfigurationWithMimeTypeDetection() throws Exception {
standardSubsystemTest("modeshape-mime-type-detection.xml");
}
@Test
public void testConfigurationWithReindexing() throws Exception {
standardSubsystemTest("modeshape-reindexing.xml");
}
@Test
public void testConfigurationWithPersistence() throws Exception {
standardSubsystemTest("modeshape-persistence-config.xml");
}
@Test
public void testConfigurationWithCustomDependencies() throws Exception {
standardSubsystemTest("modeshape-repository-dependencies-config.xml");
}
@Test
public void testSampleConfigurationModel() throws Exception {
List<ModelNode> nodes = parse(readResource("modeshape-sample-config.xml"));
assertEquals(7, nodes.size());
ModelNode subsystem = nodes.get(0);
assertNode(subsystem.get(OP_ADDR), new KeyValue(SUBSYSTEM, SUBSYSTEM_NAME));
ModelNode repo1 = nodes.get(1);
assertNode(repo1.get(OP_ADDR), new KeyValue(SUBSYSTEM, SUBSYSTEM_NAME), new KeyValue(REPOSITORY, "sample1"));
assertEquals(repo1.get(ModelKeys.JNDI_NAME).asString(), "jcr/local/modeshape_repo1");
ModelNode seq1 = nodes.get(2);
assertNode(seq1.get(OP_ADDR),
new KeyValue(SUBSYSTEM, SUBSYSTEM_NAME),
new KeyValue(REPOSITORY, "sample1"),
new KeyValue("sequencer", "modeshape-sequencer-ddl"));
assertEquals(seq1.get(ModelKeys.CLASSNAME).asString(), "ddl");
assertEquals(seq1.get(ModelKeys.PATH_EXPRESSIONS).asList().get(0).asString(), "//a/b");
ModelNode seq2 = nodes.get(3);
assertNode(seq2.get(OP_ADDR),
new KeyValue(SUBSYSTEM, SUBSYSTEM_NAME),
new KeyValue(REPOSITORY, "sample1"),
new KeyValue("sequencer", "modeshape-sequencer-java"));
assertEquals(seq2.get(ModelKeys.CLASSNAME).asString(), "java");
assertEquals(seq2.get(ModelKeys.PATH_EXPRESSIONS).asList().get(0).asString(), "//a/b");
assertNode(seq2.get(ModelKeys.PROPERTIES), new KeyValue("extra1", "value1"), new KeyValue("extra2", "2"));
ModelNode repo2 = nodes.get(4);
assertNode(repo2.get(OP_ADDR), new KeyValue(SUBSYSTEM, SUBSYSTEM_NAME), new KeyValue(REPOSITORY, "sample2"));
ModelNode seq3 = nodes.get(5);
assertNode(seq3.get(OP_ADDR),
new KeyValue(SUBSYSTEM, SUBSYSTEM_NAME),
new KeyValue(REPOSITORY, "sample2"),
new KeyValue("sequencer", "modeshape-sequencer-ddl"));
assertEquals(seq3.get(ModelKeys.CLASSNAME).asString(), "ddl");
assertEquals(seq3.get(ModelKeys.PATH_EXPRESSIONS).asList().get(0).asString(), "//a/b/");
assertEquals(seq3.get(ModelKeys.PATH_EXPRESSIONS).asList().get(1).asString(), "//a/b2/");
ModelNode seq4 = nodes.get(6);
assertNode(seq4.get(OP_ADDR),
new KeyValue(SUBSYSTEM, SUBSYSTEM_NAME),
new KeyValue(REPOSITORY, "sample2"),
new KeyValue("sequencer", "modeshape-sequencer-java"));
assertEquals(seq4.get(ModelKeys.CLASSNAME).asString(), "java");
assertEquals(seq4.get(ModelKeys.PATH_EXPRESSIONS).asList().get(0).asString(), "//a/b");
}
@Test
public void testAddRemoveRepository() throws Exception {
String subsystemXml = readResource("modeshape-sample-config.xml");
validate(subsystemXml);
KernelServices services = initKernel(subsystemXml);
PathAddress addr = PathAddress.pathAddress(PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME));
// look at current query engines make sure there are only two from configuration.
ModelNode read = new ModelNode();
read.get(OP).set("read-children-names");
read.get(OP_ADDR).set(addr.toModelNode());
read.get(CHILD_TYPE).set("repository");
ModelNode result = services.executeOperation(read);
Assert.assertEquals(SUCCESS, result.get(OUTCOME).asString());
List<String> opNames = getList(result);
assertEquals(2, opNames.size());
String[] ops = {"sample1", "sample2"};
assertEquals(Arrays.asList(ops), opNames);
// add repository
ModelNode addOp = new ModelNode();
addOp.get(OP).set("add");
addOp.get(OP_ADDR).set(addr.toModelNode().add("repository", "myrepository")); //$NON-NLS-1$;
addOp.get("jndi-name").set("jcr:local:myrepository");
result = services.executeOperation(addOp);
Assert.assertEquals(SUCCESS, result.get(OUTCOME).asString());
result = services.executeOperation(read);
Assert.assertEquals(SUCCESS, result.get(OUTCOME).asString());
opNames = getList(result);
assertEquals(3, opNames.size());
String[] ops2 = {"myrepository", "sample1", "sample2"};
assertEquals(Arrays.asList(ops2), opNames);
}
private KernelServices initKernel( String subsystemXml ) throws Exception {
return super.createKernelServicesBuilder(super.createAdditionalInitialization()).setSubsystemXml(subsystemXml).build();
}
@Test
public void testAnonymousRolesConfiguration() throws Exception {
standardSubsystemTest("anonymous");
}
@Test
public void testSequencingConfiguration() throws Exception {
standardSubsystemTest("sequencing");
}
@Test
public void testTextExtractionConfiguration() throws Exception {
standardSubsystemTest("text-extraction");
}
@Test
public void testSchema() throws Exception {
String subsystemXml = readResource("modeshape-sample-config.xml");
validate(subsystemXml);
KernelServices services = initKernel(subsystemXml);
// Get the model and the persisted xml from the controller
services.readWholeModel();
String marshalled = services.getPersistedSubsystemXml();
validate(marshalled);
}
@Override
protected String getSubsystemXsdPath() throws Exception {
return "schema/modeshape_3_0.xsd";
}
@Override
protected String[] getSubsystemTemplatePaths() throws IOException {
return new String[]{"modeshape-full-config.xml"};
}
private void validate( String marshalled ) throws SAXException, IOException {
URL xsdURL = Thread.currentThread().getContextClassLoader().getResource("schema/modeshape_3_0.xsd");
// System.out.println(marshalled);
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema = factory.newSchema(xsdURL);
Validator validator = schema.newValidator();
Source source = new StreamSource(new ByteArrayInputStream(marshalled.getBytes()));
validator.setErrorHandler(new ErrorHandler() {
@Override
public void warning( SAXParseException exception ) {
fail(exception.getMessage());
}
@Override
public void fatalError( SAXParseException exception ) {
fail(exception.getMessage());
}
@Override
public void error( SAXParseException exception ) {
if (exception.getMessage().contains("cvc-enumeration-valid")) return;
if (exception.getMessage().contains("cvc-type")) return;
fail(exception.getMessage());
}
});
validator.validate(source);
}
private static List<String> getList( ModelNode operationResult ) {
if (!operationResult.hasDefined("result")) {
return Collections.emptyList();
}
List<ModelNode> nodeList = operationResult.get("result").asList();
if (nodeList.isEmpty()) {
return Collections.emptyList();
}
List<String> list = new ArrayList<String>(nodeList.size());
for (ModelNode node : nodeList) {
list.add(node.asString());
}
return list;
}
private void assertNode( ModelNode node,
KeyValue... values ) {
assertTrue(values.length > 0);
if (node.getType() == ModelType.LIST) {
List<ModelNode> modelNodes = node.asList();
Assert.assertEquals(values.length, modelNodes.size());
for (int i = 0; i < modelNodes.size(); i++) {
assertNode(modelNodes.get(i), values[i]);
}
} else {
values[0].assertEquals(node);
}
}
private class KeyValue {
private String key;
private Object value;
private ModelType type;
protected KeyValue( String key,
Object value,
ModelType type ) {
this.key = key;
this.value = value;
this.type = type;
}
protected KeyValue( String key,
Object value ) {
this(key, value, ModelType.STRING);
}
protected void assertEquals( ModelNode node ) {
Object actualInstance = null;
assertTrue(key + " not present on ModelNode", node.has(key));
switch (type) {
case BIG_DECIMAL: {
actualInstance = node.get(key).asBigDecimal();
break;
}
case STRING: {
actualInstance = node.get(key).asString();
break;
}
case LONG: {
actualInstance = node.get(key).asLong();
break;
}
case BOOLEAN: {
actualInstance = node.get(key).asBoolean();
break;
}
default: {
actualInstance = node.get(key).asString();
}
}
Assert.assertEquals("Unexpected value in model node under " + key, value, actualInstance);
}
}
}