This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.testing.sling-mock-1.6.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-sling-mock.git
commit 3c296a7cfce23fb2c4dc76bd49e0130c749730e6 Author: Stefan Seifert <[email protected]> AuthorDate: Mon Sep 28 22:16:38 2015 +0000 SLING-5064 manual registration of node types no longer required; register namespaces and node types centrally depending on NodeTypeMode git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/testing/mocks/sling-mock@1705787 13f79535-47bb-0310-9956-ffa450edef68 --- .../mock/sling/MockJcrResourceResolverAdapter.java | 14 - .../apache/sling/testing/mock/sling/MockSling.java | 25 + .../mock/sling/NodeTypeDefinitionScanner.java | 558 +++++++++++++++++++++ .../sling/{package-info.java => NodeTypeMode.java} | 24 +- .../testing/mock/sling/ResourceResolverType.java | 23 +- .../sling/context/NodeTypeDefinitionScanner.java | 193 ------- .../sling/testing/mock/sling/package-info.java | 2 +- .../NodeTypeDefinitionScannerTest.java | 3 +- .../resource/MultipleResourceResolverTest.java | 2 +- .../loader/AbstractContentLoaderJsonDamTest.java | 23 +- .../loader/AbstractContentLoaderJsonTest.java | 23 +- .../sling/resource/AbstractJcrNamespaceTest.java | 33 +- .../AbstractMultipleResourceResolverTest.java | 14 +- src/test/resources/SLING-INF/nodetypes/app.cnd | 26 + 14 files changed, 681 insertions(+), 282 deletions(-) diff --git a/src/main/java/org/apache/sling/testing/mock/sling/MockJcrResourceResolverAdapter.java b/src/main/java/org/apache/sling/testing/mock/sling/MockJcrResourceResolverAdapter.java index 4ec0f72..87705b6 100644 --- a/src/main/java/org/apache/sling/testing/mock/sling/MockJcrResourceResolverAdapter.java +++ b/src/main/java/org/apache/sling/testing/mock/sling/MockJcrResourceResolverAdapter.java @@ -18,10 +18,7 @@ */ package org.apache.sling.testing.mock.sling; -import javax.jcr.NamespaceRegistry; import javax.jcr.Repository; -import javax.jcr.RepositoryException; -import javax.jcr.Session; import org.apache.sling.api.resource.ResourceResolverFactory; import org.apache.sling.jcr.api.SlingRepository; @@ -41,17 +38,6 @@ class MockJcrResourceResolverAdapter implements ResourceResolverTypeAdapter { @Override public SlingRepository newSlingRepository() { Repository repository = MockJcr.newRepository(); - - try { - Session session = repository.login(); - NamespaceRegistry namespaceRegistry = session.getWorkspace().getNamespaceRegistry(); - namespaceRegistry.registerNamespace("sling", "http://sling.apache.org/jcr/sling/1.0"); - session.logout(); - } - catch (RepositoryException ex) { - throw new RuntimeException("Unable to register namespaces in JCR Mock repository.", ex); - } - return new MockSlingRepository(repository); } diff --git a/src/main/java/org/apache/sling/testing/mock/sling/MockSling.java b/src/main/java/org/apache/sling/testing/mock/sling/MockSling.java index dcdebcb..638d4c9 100644 --- a/src/main/java/org/apache/sling/testing/mock/sling/MockSling.java +++ b/src/main/java/org/apache/sling/testing/mock/sling/MockSling.java @@ -18,6 +18,9 @@ */ package org.apache.sling.testing.mock.sling; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.adapter.SlingAdaptable; @@ -76,12 +79,34 @@ public final class MockSling { factory = new MockNoneResourceResolverFactory(bundleContext); } else { + registerJcrNodeTypes(repository, type); factory = new MockJcrResourceResolverFactory(repository, bundleContext); } } return factory; } + /** + * Registers all JCR node types found in classpath. + * @param slingRepository Sling repository + */ + @SuppressWarnings("deprecation") + private static void registerJcrNodeTypes(final SlingRepository slingRepository, final ResourceResolverType type) { + Session session = null; + try { + session = slingRepository.loginAdministrative(null); + NodeTypeDefinitionScanner.get().register(session, type.getNodeTypeMode()); + } + catch (RepositoryException ex) { + throw new RuntimeException("Error registering JCR nodetypes: " + ex.getMessage(), ex); + } + finally { + if (session != null) { + session.logout(); + } + } + } + private static ResourceResolverTypeAdapter getResourceResolverTypeAdapter(final ResourceResolverType type) { try { Class clazz = Class.forName(type.getResourceResolverTypeAdapterClass()); diff --git a/src/main/java/org/apache/sling/testing/mock/sling/NodeTypeDefinitionScanner.java b/src/main/java/org/apache/sling/testing/mock/sling/NodeTypeDefinitionScanner.java new file mode 100644 index 0000000..948fb34 --- /dev/null +++ b/src/main/java/org/apache/sling/testing/mock/sling/NodeTypeDefinitionScanner.java @@ -0,0 +1,558 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.sling.testing.mock.sling; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import javax.jcr.NamespaceRegistry; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.Value; +import javax.jcr.ValueFactory; +import javax.jcr.Workspace; +import javax.jcr.nodetype.NodeDefinition; +import javax.jcr.nodetype.NodeDefinitionTemplate; +import javax.jcr.nodetype.NodeType; +import javax.jcr.nodetype.NodeTypeDefinition; +import javax.jcr.nodetype.NodeTypeIterator; +import javax.jcr.nodetype.NodeTypeManager; +import javax.jcr.nodetype.NodeTypeTemplate; +import javax.jcr.nodetype.PropertyDefinition; +import javax.jcr.nodetype.PropertyDefinitionTemplate; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.jackrabbit.commons.cnd.CndImporter; +import org.apache.jackrabbit.commons.cnd.CompactNodeTypeDefReader; +import org.apache.jackrabbit.commons.cnd.DefinitionBuilderFactory; +import org.apache.jackrabbit.commons.cnd.TemplateBuilderFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Singleton class that fetches all node type definitions from OSGi bundle MANIFEST.MF files + * with "Sling-Nodetypes" definitions in the classpath. + * Additionally it support registering them to a JCR repository. + */ +public final class NodeTypeDefinitionScanner { + + private static final NodeTypeDefinitionScanner SINGLETON = new NodeTypeDefinitionScanner(); + + private static final int MAX_ITERATIONS = 5; + + private static final Logger log = LoggerFactory.getLogger(NodeTypeDefinitionScanner.class); + + private final List<String> nodeTypeDefinitions; + + private NodeTypeDefinitionScanner() { + nodeTypeDefinitions = findeNodeTypeDefinitions(); + } + + /** + * @return Node type definitions found in classpath as registered in OSGi bundle headers + */ + public List<String> getNodeTypeDefinitions() { + return nodeTypeDefinitions; + } + + /** + * Registers node types found in classpath in JCR repository. + * @param session Session + */ + public void register(Session session, NodeTypeMode nodeTypeMode) throws RepositoryException { + List<String> nodeTypeResources = getNodeTypeDefinitions(); + register(session, nodeTypeResources, nodeTypeMode); + } + + /** + * Registers node types found in classpath in JCR repository. + * @param session Session + * @param nodeTypeResources List of classpath resource URLs pointing to node type definitions + */ + public void register(Session session, List<String> nodeTypeResources, NodeTypeMode nodeTypeMode) throws RepositoryException { + switch (nodeTypeMode) { + case NOT_SUPPORTED: + // do nothing + break; + case NAMESPACES_ONLY: + registerNamespaces(session, nodeTypeResources); + break; + case NODETYPES_REQUIRED: + registerNodeTypes(session, nodeTypeResources); + break; + default: + throw new IllegalArgumentException("Node type mode not supported: " + nodeTypeMode); + } + } + + /** + * Registers only the namespaces found in node type definitions in classpath in JCR repository. + * @param session Session + * @param nodeTypeResources List of classpath resource URLs pointing to node type definitions + */ + private void registerNamespaces(Session session, List<String> nodeTypeResources) throws RepositoryException { + ClassLoader classLoader = getClass().getClassLoader(); + Workspace workspace = session.getWorkspace(); + NamespaceRegistry namespaceRegistry = workspace.getNamespaceRegistry(); + ValueFactory valueFactory = session.getValueFactory(); + + DefinitionBuilderFactory<NodeTypeTemplate, NamespaceRegistry> factory = + new TemplateBuilderFactory(new DummyNodeTypeManager(), valueFactory, namespaceRegistry); + + for (String nodeTypeResource : nodeTypeResources) { + InputStream is = classLoader.getResourceAsStream(nodeTypeResource); + if (is == null) { + continue; + } + try { + Reader reader = new InputStreamReader(is); + CompactNodeTypeDefReader<NodeTypeTemplate, NamespaceRegistry> cndReader + = new CompactNodeTypeDefReader<NodeTypeTemplate, NamespaceRegistry>(reader, nodeTypeResource, factory); + NamespaceRegistry mapping = cndReader.getNamespaceMapping(); + for (int i=0; i<mapping.getURIs().length; i++) { + String uri = mapping.getURIs()[i]; + String prefix = mapping.getPrefix(uri); + try { + namespaceRegistry.registerNamespace(prefix, uri); + } + catch (RepositoryException ex) { + // ignore + } + } + } + catch (Throwable ex) { + log.warn("Unable to parse node type definition: " + nodeTypeResource, ex); + } + finally { + IOUtils.closeQuietly(is); + } + } + + } + + /** + * Registers node types found in classpath in JCR repository. + * @param session Session + * @param nodeTypeResources List of classpath resource URLs pointing to node type definitions + */ + private void registerNodeTypes(Session session, List<String> nodeTypeResources) throws RepositoryException { + ClassLoader classLoader = getClass().getClassLoader(); + Workspace workspace = session.getWorkspace(); + NodeTypeManager nodeTypeManager = workspace.getNodeTypeManager(); + NamespaceRegistry namespaceRegistry = workspace.getNamespaceRegistry(); + ValueFactory valueFactory = session.getValueFactory(); + + // try registering node types multiple times because the exact order is not known + int iteration = 0; + List<String> remainingNodeTypeResources = new ArrayList<String>(nodeTypeResources); + while (!remainingNodeTypeResources.isEmpty()) { + registerNodeTypesAndRemoveSucceeds(remainingNodeTypeResources, classLoader, nodeTypeManager, namespaceRegistry, valueFactory, false); + iteration++; + if (iteration >= MAX_ITERATIONS) { + break; + } + } + if (!remainingNodeTypeResources.isEmpty()) { + registerNodeTypesAndRemoveSucceeds(remainingNodeTypeResources, classLoader, nodeTypeManager, namespaceRegistry, valueFactory, true); + } + } + + /** + * Register node types found in classpath in JCR repository, and remove those that succeeded to register from the list. + * @param nodeTypeResources List of nodetype classpath resources + * @param classLoader + * @param nodeTypeManager + * @param namespaceRegistry + * @param valueFactory + * @param logError if true, and error is logged if node type registration failed. Otherwise it is ignored. + */ + private void registerNodeTypesAndRemoveSucceeds(List<String> nodeTypeResources, ClassLoader classLoader, + NodeTypeManager nodeTypeManager, NamespaceRegistry namespaceRegistry, ValueFactory valueFactory, + boolean logError) { + Iterator<String> nodeTypeResourcesIterator = nodeTypeResources.iterator(); + while (nodeTypeResourcesIterator.hasNext()) { + String nodeTypeResource = nodeTypeResourcesIterator.next(); + InputStream is = classLoader.getResourceAsStream(nodeTypeResource); + if (is == null) { + continue; + } + try { + Reader reader = new InputStreamReader(is); + CndImporter.registerNodeTypes(reader, nodeTypeResource, nodeTypeManager, namespaceRegistry, valueFactory, false); + nodeTypeResourcesIterator.remove(); + } + catch (Throwable ex) { + if (logError) { + log.warn("Unable to register node type: " + nodeTypeResource, ex); + } + } + finally { + IOUtils.closeQuietly(is); + } + } + } + + /** + * Find all node type definition classpath paths by searching all MANIFEST.MF files in the classpath and reading + * the paths from the "Sling-Nodetypes" entry. + * The order of the paths from each entry is preserved, but the overall order when multiple bundles define such an entry + * is not deterministic and may not be correct according to the dependencies between the node type definitions. + * @return List of node type definition class paths + */ + private static List<String> findeNodeTypeDefinitions() { + List<String> nodeTypeDefinitions = new ArrayList<String>(); + try { + Enumeration<URL> resEnum = NodeTypeDefinitionScanner.class.getClassLoader().getResources(JarFile.MANIFEST_NAME); + while (resEnum.hasMoreElements()) { + try { + URL url = (URL)resEnum.nextElement(); + InputStream is = url.openStream(); + if (is != null) { + try { + Manifest manifest = new Manifest(is); + Attributes mainAttribs = manifest.getMainAttributes(); + String nodeTypeDefinitionList = mainAttribs.getValue("Sling-Nodetypes"); + String[] nodeTypeDefinitionArray = StringUtils.split(nodeTypeDefinitionList, ","); + if (nodeTypeDefinitionArray != null) { + for (String nodeTypeDefinition : nodeTypeDefinitionArray) { + if (!StringUtils.isBlank(nodeTypeDefinition)) { + nodeTypeDefinitions.add(StringUtils.trim(nodeTypeDefinition)); + } + } + } + } + finally { + is.close(); + } + } + } + catch (Throwable ex) { + log.warn("Unable to read JAR manifest.", ex); + } + } + } + catch (IOException ex2) { + log.warn("Unable to read JAR manifests.", ex2); + } + return nodeTypeDefinitions; + } + + public static NodeTypeDefinitionScanner get() { + return SINGLETON; + } + + + /** + * Some dummy classes to allow usage of CompactNodeTypeDefReader with underlying JCR mock + */ + private static class DummyNodeTypeManager implements NodeTypeManager { + @Override + public NodeType getNodeType(String nodeTypeName) { + return null; + } + @Override + public boolean hasNodeType(String name) { + return false; + } + @Override + public NodeTypeIterator getAllNodeTypes() { + return null; + } + @Override + public NodeTypeIterator getPrimaryNodeTypes() { + return null; + } + @Override + public NodeTypeIterator getMixinNodeTypes() { + return null; + } + @Override + public NodeTypeTemplate createNodeTypeTemplate() { + return new DummyNodeTypeTemplate(); + } + @Override + public NodeTypeTemplate createNodeTypeTemplate(NodeTypeDefinition ntd) { + return new DummyNodeTypeTemplate(); + } + @Override + public NodeDefinitionTemplate createNodeDefinitionTemplate() { + return new DummyNodeDefinitionTemplate(); + } + @Override + public PropertyDefinitionTemplate createPropertyDefinitionTemplate() { + return new DummyPropertyDefinitionTemplate(); + } + @Override + public NodeType registerNodeType(NodeTypeDefinition ntd, boolean allowUpdate) { + return null; + } + @Override + public NodeTypeIterator registerNodeTypes(NodeTypeDefinition[] ntds, boolean allowUpdate) { + return null; + } + @Override + public void unregisterNodeType(String name) { + } + @Override + public void unregisterNodeTypes(String[] names) { + } + } + + private static class DummyNodeTypeTemplate implements NodeTypeTemplate { + @Override + public String getName() { + return null; + } + @Override + public String[] getDeclaredSupertypeNames() { + return null; + } + @Override + public boolean isAbstract() { + return false; + } + @Override + public boolean isMixin() { + return false; + } + @Override + public boolean hasOrderableChildNodes() { + return false; + } + @Override + public boolean isQueryable() { + return false; + } + @Override + public String getPrimaryItemName() { + return null; + } + @Override + public PropertyDefinition[] getDeclaredPropertyDefinitions() { + return null; + } + @Override + public NodeDefinition[] getDeclaredChildNodeDefinitions() { + return null; + } + @Override + public void setName(String name) { + } + @Override + public void setDeclaredSuperTypeNames(String[] names) { + } + @Override + public void setAbstract(boolean abstractStatus) { + } + @Override + public void setMixin(boolean mixin) { + } + @Override + public void setOrderableChildNodes(boolean orderable) { + } + @Override + public void setPrimaryItemName(String name) { + } + @Override + public void setQueryable(boolean queryable) { + } + @Override + public List getPropertyDefinitionTemplates() { + return new ArrayList(); + } + @Override + public List getNodeDefinitionTemplates() { + return new ArrayList(); + } + } + + private static class DummyNodeDefinitionTemplate implements NodeDefinitionTemplate { + @Override + public NodeType[] getRequiredPrimaryTypes() { + return null; + } + @Override + public String[] getRequiredPrimaryTypeNames() { + return null; + } + @Override + public NodeType getDefaultPrimaryType() { + return null; + } + @Override + public String getDefaultPrimaryTypeName() { + return null; + } + @Override + public boolean allowsSameNameSiblings() { + return false; + } + @Override + public NodeType getDeclaringNodeType() { + return null; + } + @Override + public String getName() { + return null; + } + @Override + public boolean isAutoCreated() { + return false; + } + @Override + public boolean isMandatory() { + return false; + } + @Override + public int getOnParentVersion() { + return 0; + } + @Override + public boolean isProtected() { + return false; + } + @Override + public void setName(String name) { + } + @Override + public void setAutoCreated(boolean autoCreated) { + } + @Override + public void setMandatory(boolean mandatory) { + } + @Override + public void setOnParentVersion(int opv) { + } + @Override + public void setProtected(boolean protectedStatus) { + } + @Override + public void setRequiredPrimaryTypeNames(String[] names) { + } + @Override + public void setDefaultPrimaryTypeName(String name) { + } + @Override + public void setSameNameSiblings(boolean allowSameNameSiblings) { + } + } + + private static class DummyPropertyDefinitionTemplate implements PropertyDefinitionTemplate { + @Override + public int getRequiredType() { + return 0; + } + @Override + public String[] getValueConstraints() { + return null; + } + @Override + public Value[] getDefaultValues() { + return null; + } + @Override + public boolean isMultiple() { + return false; + } + @Override + public String[] getAvailableQueryOperators() { + return null; + } + @Override + public boolean isFullTextSearchable() { + return false; + } + @Override + public boolean isQueryOrderable() { + return false; + } + @Override + public NodeType getDeclaringNodeType() { + return null; + } + @Override + public String getName() { + return null; + } + @Override + public boolean isAutoCreated() { + return false; + } + @Override + public boolean isMandatory() { + return false; + } + @Override + public int getOnParentVersion() { + return 0; + } + @Override + public boolean isProtected() { + return false; + } + @Override + public void setName(String name) { + } + @Override + public void setAutoCreated(boolean autoCreated) { + } + @Override + public void setMandatory(boolean mandatory) { + } + @Override + public void setOnParentVersion(int opv) { + } + @Override + public void setProtected(boolean protectedStatus) { + } + @Override + public void setRequiredType(int type) { + } + @Override + public void setValueConstraints(String[] constraints) { + } + @Override + public void setDefaultValues(Value[] defaultValues) { + } + @Override + public void setMultiple(boolean multiple) { + } + @Override + public void setAvailableQueryOperators(String[] operators) { + } + @Override + public void setFullTextSearchable(boolean fullTextSearchable) { + } + @Override + public void setQueryOrderable(boolean queryOrderable) { + } + } + +} diff --git a/src/main/java/org/apache/sling/testing/mock/sling/package-info.java b/src/main/java/org/apache/sling/testing/mock/sling/NodeTypeMode.java similarity index 65% copy from src/main/java/org/apache/sling/testing/mock/sling/package-info.java copy to src/main/java/org/apache/sling/testing/mock/sling/NodeTypeMode.java index 41f438c..9587feb 100644 --- a/src/main/java/org/apache/sling/testing/mock/sling/package-info.java +++ b/src/main/java/org/apache/sling/testing/mock/sling/NodeTypeMode.java @@ -16,8 +16,26 @@ * specific language governing permissions and limitations * under the License. */ +package org.apache.sling.testing.mock.sling; + /** - * Mock implementation of selected Sling APIs. + * How to handle node types for different {@link ResourceResolverType} types. */ [email protected]("1.4") -package org.apache.sling.testing.mock.sling; +public enum NodeTypeMode { + + /** + * Neither registration of namespaces or node types required (no underlying JCR). + */ + NOT_SUPPORTED, + + /** + * Namespaces have to be registered, but nodetypes are not supported. + */ + NAMESPACES_ONLY, + + /** + * Nodetypes including namespaces have to be registered. + */ + NODETYPES_REQUIRED + +} diff --git a/src/main/java/org/apache/sling/testing/mock/sling/ResourceResolverType.java b/src/main/java/org/apache/sling/testing/mock/sling/ResourceResolverType.java index a32f86e..bf77a6f 100644 --- a/src/main/java/org/apache/sling/testing/mock/sling/ResourceResolverType.java +++ b/src/main/java/org/apache/sling/testing/mock/sling/ResourceResolverType.java @@ -37,7 +37,7 @@ public enum ResourceResolverType { * <li>This resource resolver type is very fast.</li> * </ul> */ - RESOURCERESOLVER_MOCK(RRMockMockResourceResolverAdapter.class.getName(), null), + RESOURCERESOLVER_MOCK(RRMockMockResourceResolverAdapter.class.getName(), null, NodeTypeMode.NOT_SUPPORTED), /** * Uses a simple JCR "in-memory" mock as underlying repository. @@ -51,7 +51,7 @@ public enum ResourceResolverType { * <li>This resource resolver type is quite fast.</li> * </ul> */ - JCR_MOCK(MockJcrResourceResolverAdapter.class.getName(), null), + JCR_MOCK(MockJcrResourceResolverAdapter.class.getName(), null, NodeTypeMode.NAMESPACES_ONLY), /** * Uses a real JCR Jackrabbit repository. @@ -65,7 +65,7 @@ public enum ResourceResolverType { * </ul> */ JCR_JACKRABBIT("org.apache.sling.testing.mock.sling.jackrabbit.JackrabbitMockResourceResolverAdapter", - "org.apache.sling:org.apache.sling.testing.sling-mock-jackrabbit"), + "org.apache.sling:org.apache.sling.testing.sling-mock-jackrabbit", NodeTypeMode.NODETYPES_REQUIRED), /** * Uses a real JCR Jackrabbit Oak repository. @@ -79,7 +79,7 @@ public enum ResourceResolverType { * </ul> */ JCR_OAK("org.apache.sling.testing.mock.sling.oak.OakMockResourceResolverAdapter", - "org.apache.sling:org.apache.sling.testing.sling-mock-jackrabbit-oak"), + "org.apache.sling:org.apache.sling.testing.sling-mock-jackrabbit-oak", NodeTypeMode.NODETYPES_REQUIRED), /** * Provides resource resolver environment without any ResourceProvider. @@ -89,16 +89,20 @@ public enum ResourceResolverType { * <li>The performance of this resource resolver type depends on the resource provider registered.</li> * </ul> */ - NONE(MockNoneResourceResolverAdapter.class.getName(), null); + NONE(MockNoneResourceResolverAdapter.class.getName(), null, NodeTypeMode.NOT_SUPPORTED); private final String resourceResolverTypeAdapterClass; private final String artifactCoordinates; + private final NodeTypeMode nodeTypeMode; + - private ResourceResolverType(final String resourceResolverTypeAdapterClass, final String artifactCoordinates) { + private ResourceResolverType(final String resourceResolverTypeAdapterClass, final String artifactCoordinates, + final NodeTypeMode nodeTypeMode) { this.resourceResolverTypeAdapterClass = resourceResolverTypeAdapterClass; this.artifactCoordinates = artifactCoordinates; + this.nodeTypeMode = nodeTypeMode; } String getResourceResolverTypeAdapterClass() { @@ -109,4 +113,11 @@ public enum ResourceResolverType { return this.artifactCoordinates; } + /** + * @return How JCR namespaces and node types have to be handled. + */ + public NodeTypeMode getNodeTypeMode() { + return nodeTypeMode; + } + } diff --git a/src/main/java/org/apache/sling/testing/mock/sling/context/NodeTypeDefinitionScanner.java b/src/main/java/org/apache/sling/testing/mock/sling/context/NodeTypeDefinitionScanner.java deleted file mode 100644 index 441c0c8..0000000 --- a/src/main/java/org/apache/sling/testing/mock/sling/context/NodeTypeDefinitionScanner.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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.apache.sling.testing.mock.sling.context; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; -import java.util.jar.Attributes; -import java.util.jar.JarFile; -import java.util.jar.Manifest; - -import javax.jcr.NamespaceRegistry; -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.ValueFactory; -import javax.jcr.Workspace; -import javax.jcr.nodetype.NodeTypeManager; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.jackrabbit.commons.cnd.CndImporter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Singleton class that fetches all node type definitions from OSGi bundle MANIFEST.MF files - * with "Sling-Nodetypes" definitions in the classpath. - * Additionally it support registering them to a JCR repository. - */ -public final class NodeTypeDefinitionScanner { - - private static final NodeTypeDefinitionScanner SINGLETON = new NodeTypeDefinitionScanner(); - - private static final int MAX_ITERATIONS = 5; - - private static final Logger log = LoggerFactory.getLogger(NodeTypeDefinitionScanner.class); - - private final List<String> nodeTypeDefinitions; - - private NodeTypeDefinitionScanner() { - nodeTypeDefinitions = findeNodeTypeDefinitions(); - } - - /** - * @return Node type definitions found in classpath as registered in OSGi bundle headers - */ - public List<String> getNodeTypeDefinitions() { - return nodeTypeDefinitions; - } - - /** - * Registers node types found in classpath in JCR repository. - * @param session Session - */ - public void register(Session session) throws RepositoryException { - List<String> nodeTypeResources = getNodeTypeDefinitions(); - register(session, nodeTypeResources); - } - - /** - * Registers node types found in classpath in JCR repository. - * @param session Session - * @param nodeTypeResources List of classpath resource URLs pointing to node type definitions - */ - public void register(Session session, List<String> nodeTypeResources) throws RepositoryException { - ClassLoader classLoader = getClass().getClassLoader(); - Workspace workspace = session.getWorkspace(); - NodeTypeManager nodeTypeManager = workspace.getNodeTypeManager(); - NamespaceRegistry namespaceRegistry = workspace.getNamespaceRegistry(); - ValueFactory valueFactory = session.getValueFactory(); - - // try registering node types multiple times because the eyact order is not known - int iteration = 0; - List<String> remainingNodeTypeResources = new ArrayList<String>(nodeTypeResources); - while (!remainingNodeTypeResources.isEmpty()) { - registerAndRemoveSucceeds(remainingNodeTypeResources, classLoader, nodeTypeManager, namespaceRegistry, valueFactory, false); - iteration++; - if (iteration >= MAX_ITERATIONS) { - break; - } - } - if (!remainingNodeTypeResources.isEmpty()) { - registerAndRemoveSucceeds(remainingNodeTypeResources, classLoader, nodeTypeManager, namespaceRegistry, valueFactory, true); - } - } - - /** - * Register node types found in classpath in JCR repository, and remove those that succeeded to register from the list. - * @param nodeTypeResources List of nodetype classpath resources - * @param classLoader - * @param nodeTypeManager - * @param namespaceRegistry - * @param valueFactory - * @param logError if true, and error is logged if node type registration failed. Otherwise it is ignored. - */ - private void registerAndRemoveSucceeds(List<String> nodeTypeResources, ClassLoader classLoader, - NodeTypeManager nodeTypeManager, NamespaceRegistry namespaceRegistry, ValueFactory valueFactory, - boolean logError) { - Iterator<String> nodeTypeResourcesIterator = nodeTypeResources.iterator(); - while (nodeTypeResourcesIterator.hasNext()) { - String nodeTypeResource = nodeTypeResourcesIterator.next(); - InputStream is = classLoader.getResourceAsStream(nodeTypeResource); - if (is == null) { - continue; - } - try { - Reader reader = new InputStreamReader(is); - CndImporter.registerNodeTypes(reader, nodeTypeResource, nodeTypeManager, namespaceRegistry, valueFactory, false); - nodeTypeResourcesIterator.remove(); - } - catch (Throwable ex) { - if (logError) { - log.warn("Unable to register node type: " + nodeTypeResource, ex); - } - } - finally { - IOUtils.closeQuietly(is); - } - } - } - - /** - * Find all node type definition classpath paths by searching all MANIFEST.MF files in the classpath and reading - * the paths from the "Sling-Nodetypes" entry. - * The order of the paths from each entry is preserved, but the overall order when multiple bundles define such an entry - * is not deterministic and may not be correct according to the dependencies between the node type definitions. - * @return List of node type definition class paths - */ - private static List<String> findeNodeTypeDefinitions() { - List<String> nodeTypeDefinitions = new ArrayList<String>(); - try { - Enumeration<URL> resEnum = NodeTypeDefinitionScanner.class.getClassLoader().getResources(JarFile.MANIFEST_NAME); - while (resEnum.hasMoreElements()) { - try { - URL url = (URL)resEnum.nextElement(); - InputStream is = url.openStream(); - if (is != null) { - try { - Manifest manifest = new Manifest(is); - Attributes mainAttribs = manifest.getMainAttributes(); - String nodeTypeDefinitionList = mainAttribs.getValue("Sling-Nodetypes"); - String[] nodeTypeDefinitionArray = StringUtils.split(nodeTypeDefinitionList, ","); - if (nodeTypeDefinitionArray != null) { - for (String nodeTypeDefinition : nodeTypeDefinitionArray) { - if (!StringUtils.isBlank(nodeTypeDefinition)) { - nodeTypeDefinitions.add(StringUtils.trim(nodeTypeDefinition)); - } - } - } - } - finally { - is.close(); - } - } - } - catch (Throwable ex) { - log.warn("Unable to read JAR manifest.", ex); - } - } - } - catch (IOException ex2) { - log.warn("Unable to read JAR manifests.", ex2); - } - return nodeTypeDefinitions; - } - - public static NodeTypeDefinitionScanner get() { - return SINGLETON; - } - -} diff --git a/src/main/java/org/apache/sling/testing/mock/sling/package-info.java b/src/main/java/org/apache/sling/testing/mock/sling/package-info.java index 41f438c..85ce5c4 100644 --- a/src/main/java/org/apache/sling/testing/mock/sling/package-info.java +++ b/src/main/java/org/apache/sling/testing/mock/sling/package-info.java @@ -19,5 +19,5 @@ /** * Mock implementation of selected Sling APIs. */ [email protected]("1.4") [email protected]("1.5") package org.apache.sling.testing.mock.sling; diff --git a/src/test/java/org/apache/sling/testing/mock/sling/context/NodeTypeDefinitionScannerTest.java b/src/test/java/org/apache/sling/testing/mock/sling/NodeTypeDefinitionScannerTest.java similarity index 92% rename from src/test/java/org/apache/sling/testing/mock/sling/context/NodeTypeDefinitionScannerTest.java rename to src/test/java/org/apache/sling/testing/mock/sling/NodeTypeDefinitionScannerTest.java index 63bdb50..1b1f888 100644 --- a/src/test/java/org/apache/sling/testing/mock/sling/context/NodeTypeDefinitionScannerTest.java +++ b/src/test/java/org/apache/sling/testing/mock/sling/NodeTypeDefinitionScannerTest.java @@ -16,12 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.sling.testing.mock.sling.context; +package org.apache.sling.testing.mock.sling; import static org.junit.Assert.assertTrue; import java.util.List; +import org.apache.sling.testing.mock.sling.NodeTypeDefinitionScanner; import org.junit.Test; diff --git a/src/test/java/org/apache/sling/testing/mock/sling/jcrmock/resource/MultipleResourceResolverTest.java b/src/test/java/org/apache/sling/testing/mock/sling/jcrmock/resource/MultipleResourceResolverTest.java index cc02db4..2b12d79 100644 --- a/src/test/java/org/apache/sling/testing/mock/sling/jcrmock/resource/MultipleResourceResolverTest.java +++ b/src/test/java/org/apache/sling/testing/mock/sling/jcrmock/resource/MultipleResourceResolverTest.java @@ -27,5 +27,5 @@ public class MultipleResourceResolverTest extends AbstractMultipleResourceResolv protected ResourceResolverType getResourceResolverType() { return ResourceResolverType.JCR_MOCK; } - + } diff --git a/src/test/java/org/apache/sling/testing/mock/sling/loader/AbstractContentLoaderJsonDamTest.java b/src/test/java/org/apache/sling/testing/mock/sling/loader/AbstractContentLoaderJsonDamTest.java index 15bb442..9f87ba4 100644 --- a/src/test/java/org/apache/sling/testing/mock/sling/loader/AbstractContentLoaderJsonDamTest.java +++ b/src/test/java/org/apache/sling/testing/mock/sling/loader/AbstractContentLoaderJsonDamTest.java @@ -26,7 +26,6 @@ import static org.junit.Assert.assertNotNull; import java.io.IOException; import java.io.InputStream; -import javax.jcr.NamespaceRegistry; import javax.jcr.RepositoryException; import javax.jcr.Session; @@ -37,11 +36,14 @@ import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceUtil; import org.apache.sling.api.resource.ValueMap; import org.apache.sling.testing.mock.sling.MockSling; +import org.apache.sling.testing.mock.sling.NodeTypeDefinitionScanner; import org.apache.sling.testing.mock.sling.ResourceResolverType; import org.junit.After; import org.junit.Before; import org.junit.Test; +import com.google.common.collect.ImmutableList; + public abstract class AbstractContentLoaderJsonDamTest { private ResourceResolver resourceResolver; @@ -51,18 +53,13 @@ public abstract class AbstractContentLoaderJsonDamTest { protected ResourceResolver newResourceResolver() { ResourceResolver resolver = MockSling.newResourceResolver(getResourceResolverType()); - if (getResourceResolverType() == ResourceResolverType.JCR_MOCK) { - try { - // dummy namespace registrations to make sure sling JCR resolver - // does not get mixed up with the prefixes - NamespaceRegistry namespaceRegistry = resolver.adaptTo(Session.class).getWorkspace() - .getNamespaceRegistry(); - namespaceRegistry.registerNamespace("sling", "http://mock/sling"); - namespaceRegistry.registerNamespace("app", "http://mock/app"); - namespaceRegistry.registerNamespace("dam", "http://mock/dam"); - } catch (RepositoryException ex) { - throw new RuntimeException("Unable to register namespaces.", ex); - } + try { + NodeTypeDefinitionScanner.get().register(resolver.adaptTo(Session.class), + ImmutableList.of("SLING-INF/nodetypes/app.cnd"), + getResourceResolverType().getNodeTypeMode()); + } + catch (RepositoryException ex) { + throw new RuntimeException("Unable to register namespaces.", ex); } return resolver; diff --git a/src/test/java/org/apache/sling/testing/mock/sling/loader/AbstractContentLoaderJsonTest.java b/src/test/java/org/apache/sling/testing/mock/sling/loader/AbstractContentLoaderJsonTest.java index 38a3b58..4d4267f 100644 --- a/src/test/java/org/apache/sling/testing/mock/sling/loader/AbstractContentLoaderJsonTest.java +++ b/src/test/java/org/apache/sling/testing/mock/sling/loader/AbstractContentLoaderJsonTest.java @@ -26,7 +26,6 @@ import static org.junit.Assert.assertNotNull; import java.util.Calendar; import java.util.TimeZone; -import javax.jcr.NamespaceRegistry; import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; @@ -37,11 +36,14 @@ import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceUtil; import org.apache.sling.api.resource.ValueMap; import org.apache.sling.testing.mock.sling.MockSling; +import org.apache.sling.testing.mock.sling.NodeTypeDefinitionScanner; import org.apache.sling.testing.mock.sling.ResourceResolverType; import org.junit.After; import org.junit.Before; import org.junit.Test; +import com.google.common.collect.ImmutableList; + public abstract class AbstractContentLoaderJsonTest { private ResourceResolver resourceResolver; @@ -51,18 +53,13 @@ public abstract class AbstractContentLoaderJsonTest { protected ResourceResolver newResourceResolver() { ResourceResolver resolver = MockSling.newResourceResolver(getResourceResolverType()); - if (getResourceResolverType() == ResourceResolverType.JCR_MOCK) { - try { - // dummy namespace registrations to make sure sling JCR resolver - // does not get mixed up with the prefixes - NamespaceRegistry namespaceRegistry = resolver.adaptTo(Session.class).getWorkspace() - .getNamespaceRegistry(); - namespaceRegistry.registerNamespace("sling", "http://mock/sling"); - namespaceRegistry.registerNamespace("app", "http://mock/app"); - namespaceRegistry.registerNamespace("dam", "http://mock/dam"); - } catch (RepositoryException ex) { - throw new RuntimeException("Unable to register namespaces.", ex); - } + try { + NodeTypeDefinitionScanner.get().register(resolver.adaptTo(Session.class), + ImmutableList.of("SLING-INF/nodetypes/app.cnd"), + getResourceResolverType().getNodeTypeMode()); + } + catch (RepositoryException ex) { + throw new RuntimeException("Unable to register namespaces.", ex); } return resolver; diff --git a/src/test/java/org/apache/sling/testing/mock/sling/resource/AbstractJcrNamespaceTest.java b/src/test/java/org/apache/sling/testing/mock/sling/resource/AbstractJcrNamespaceTest.java index 5185419..71b93b7 100644 --- a/src/test/java/org/apache/sling/testing/mock/sling/resource/AbstractJcrNamespaceTest.java +++ b/src/test/java/org/apache/sling/testing/mock/sling/resource/AbstractJcrNamespaceTest.java @@ -21,9 +21,7 @@ package org.apache.sling.testing.mock.sling.resource; import static org.apache.sling.jcr.resource.JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY; import static org.junit.Assert.assertEquals; -import javax.jcr.NamespaceRegistry; import javax.jcr.RepositoryException; -import javax.jcr.Session; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; @@ -47,16 +45,13 @@ public abstract class AbstractJcrNamespaceTest { protected abstract ResourceResolverType getResourceResolverType(); @Test - public void testSling4362_WithSlingNamespace() throws RepositoryException { + public void testSling4362() throws RepositoryException { ResourceResolver resolver = MockSling.newResourceResolver(getResourceResolverType()); - NamespaceRegistry namespaceRegistry = resolver.adaptTo(Session.class).getWorkspace().getNamespaceRegistry(); - namespaceRegistry.registerNamespace("sling", "http://mock/sling"); - ContentLoader contentLoader = new ContentLoader(resolver); - contentLoader.json("/json-import-samples/SLING-4362.json", "/content/foo"); + contentLoader.json("/json-import-samples/SLING-4362.json", context.uniqueRoot().content() + "/foo"); - Resource resource = resolver.getResource("/content/foo"); + Resource resource = resolver.getResource(context.uniqueRoot().content() + "/foo"); ValueMap props = ResourceUtil.getValueMap(resource); assertEquals("fooType", props.get(SLING_RESOURCE_TYPE_PROPERTY)); @@ -64,29 +59,13 @@ public abstract class AbstractJcrNamespaceTest { } @Test - public void testSling4362_WithoutSlingNamespace() throws RepositoryException { - ResourceResolver resolver = MockSling.newResourceResolver(getResourceResolverType()); - - ContentLoader contentLoader = new ContentLoader(resolver); - contentLoader.json("/json-import-samples/SLING-4362.json", "/content/foo"); - - Resource resource = resolver.getResource("/content/foo"); - - ValueMap props = ResourceUtil.getValueMap(resource); - assertEquals("fooType", props.get(SLING_RESOURCE_TYPE_PROPERTY)); - - // since SLING-4773 sling namespace is readly registered in the MockJcrResourceResolverAdapter, so this will still work here - assertEquals("fooType", resource.getResourceType()); - } - - @Test - public void testSling4362_WithoutSlingNamespace_ViaContextRule() throws RepositoryException { + public void testSling4362_ViaContextRule() throws RepositoryException { ResourceResolver resolver = context.resourceResolver(); ContentLoader contentLoader = new ContentLoader(resolver); - contentLoader.json("/json-import-samples/SLING-4362.json", "/content/foo"); + contentLoader.json("/json-import-samples/SLING-4362.json", context.uniqueRoot().content() + "/foo"); - Resource resource = resolver.getResource("/content/foo"); + Resource resource = resolver.getResource(context.uniqueRoot().content() + "/foo"); ValueMap props = ResourceUtil.getValueMap(resource); assertEquals("fooType", props.get(SLING_RESOURCE_TYPE_PROPERTY)); diff --git a/src/test/java/org/apache/sling/testing/mock/sling/resource/AbstractMultipleResourceResolverTest.java b/src/test/java/org/apache/sling/testing/mock/sling/resource/AbstractMultipleResourceResolverTest.java index cdd0aee..4944dba 100644 --- a/src/test/java/org/apache/sling/testing/mock/sling/resource/AbstractMultipleResourceResolverTest.java +++ b/src/test/java/org/apache/sling/testing/mock/sling/resource/AbstractMultipleResourceResolverTest.java @@ -18,7 +18,6 @@ */ package org.apache.sling.testing.mock.sling.resource; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -39,8 +38,9 @@ import com.google.common.collect.ImmutableMap; public abstract class AbstractMultipleResourceResolverTest { private final BundleContext bundleContext = MockOsgi.newBundleContext(); + protected abstract ResourceResolverType getResourceResolverType(); - + protected ResourceResolverFactory newResourceResolerFactory() { return MockSling.newResourceResolverFactory(getResourceResolverType(), bundleContext); } @@ -48,14 +48,8 @@ public abstract class AbstractMultipleResourceResolverTest { @Test public void testMultipleResourceResolver() throws Exception { ResourceResolverFactory factory = newResourceResolerFactory(); - ResourceResolver resolver1 = factory.getResourceResolver(ImmutableMap.<String, Object>of( - ResourceResolverFactory.USER, "user1")); - ResourceResolver resolver2 = factory.getResourceResolver(ImmutableMap.<String, Object>of( - ResourceResolverFactory.USER, "user2")); - - // validate user names - assertEquals("user1", resolver1.getAttribute(ResourceResolverFactory.USER)); - assertEquals("user2", resolver2.getAttribute(ResourceResolverFactory.USER)); + ResourceResolver resolver1 = factory.getAdministrativeResourceResolver(null); + ResourceResolver resolver2 = factory.getAdministrativeResourceResolver(null); // add a resource in resolver 1 Resource root = resolver1.getResource("/"); diff --git a/src/test/resources/SLING-INF/nodetypes/app.cnd b/src/test/resources/SLING-INF/nodetypes/app.cnd new file mode 100644 index 0000000..105d39f --- /dev/null +++ b/src/test/resources/SLING-INF/nodetypes/app.cnd @@ -0,0 +1,26 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you 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. +// +<app='http://example.com/jcr/app/1.0'> +<dam='http://example.com/jcr/dam/1.0'> + +[app:Page] > nt:unstructured +[app:PageContent] > nt:unstructured + +[dam:Asset] > nt:unstructured +[dam:AssetContent] > nt:unstructured -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
