This is an automated email from the ASF dual-hosted git repository.
enorman pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-contentloader.git
The following commit(s) were added to refs/heads/master by this push:
new 768e44c SLING-11203 Introduce requireImportProvider directive (#15)
768e44c is described below
commit 768e44ce99a223f886a21a4bd3000cf2cc2b26e6
Author: Eric Norman <[email protected]>
AuthorDate: Mon Apr 4 13:27:14 2022 -0700
SLING-11203 Introduce requireImportProvider directive (#15)
A declared requireImportProvider directive so the author can ensure that
the files are processed as intended
---
.../sling/jcr/contentloader/ImportOptions.java | 10 ++
.../apache/sling/jcr/contentloader/PathEntry.java | 58 +++++++
.../internal/BundleContentLoader.java | 55 ++++---
.../internal/BundleContentLoaderListener.java | 26 +++-
.../ContentReaderUnavailableException.java | 32 ++++
.../internal/ContentReaderWhiteboard.java | 17 ++-
.../internal/ContentReaderWhiteboardListener.java | 26 ++++
.../jcr/contentloader/ImportOptionsFactory.java | 12 +-
.../jcr/contentloader/internal/SLING11203Test.java | 170 +++++++++++++++++++++
src/test/resources/SLING-INF3/libs/app.sling11203 | 22 +++
10 files changed, 407 insertions(+), 21 deletions(-)
diff --git
a/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java
b/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java
index f66d680..4fc2e3e 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java
@@ -18,6 +18,7 @@
*/
package org.apache.sling.jcr.contentloader;
+import org.jetbrains.annotations.NotNull;
import org.osgi.annotation.versioning.ConsumerType;
/**
@@ -86,4 +87,13 @@ public abstract class ImportOptions {
*/
public abstract boolean isIgnoredImportProvider(String extension);
+ /**
+ * Check if the given entry name should require a matching registered
+ * import provider.
+ *
+ * @param name the entry name to check
+ * @return true to require an import provider, false otherwise
+ */
+ public abstract boolean isImportProviderRequired(@NotNull String name);
+
}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/PathEntry.java
b/src/main/java/org/apache/sling/jcr/contentloader/PathEntry.java
index cae3970..527b63f 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/PathEntry.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/PathEntry.java
@@ -108,6 +108,14 @@ public class PathEntry extends ImportOptions {
*/
public static final String IGNORE_CONTENT_READERS_DIRECTIVE =
"ignoreImportProviders";
+ /**
+ * The require content readers directive specifying which of the available
+ * {@link org.apache.sling.jcr.contentloader.ContentReader}s should exist
before
+ * content loading. This is a string value that defaults to the
emptystring.
+ * @since 2.5.2
+ */
+ public static final String REQUIRE_CONTENT_READERS_DIRECTIVE =
"requireImportProviders";
+
/**
* The flag "maven:mount" is not actually used by the JCR Content Loader.
It can be used
* to signal to the "fsmount" goal of the sling-maven-plugin to ignore a
certain Sling-Initial-Content entry
@@ -128,6 +136,7 @@ public class PathEntry extends ImportOptions {
CHECKIN_DIRECTIVE,
AUTOCHECKOUT_DIRECTIVE,
IGNORE_CONTENT_READERS_DIRECTIVE,
+ REQUIRE_CONTENT_READERS_DIRECTIVE,
MAVEN_MOUNT_DIRECTIVE
));
@@ -156,6 +165,9 @@ public class PathEntry extends ImportOptions {
/** Which content readers should be ignored? @since 2.0.4 */
private final List<String> ignoreContentReaders;
+ /** Which content readers should be required? @since 2.5.2 */
+ private final List<String> requireContentReaders;
+
/**
* Target path where initial content will be loaded. If it´s null then
* target node is the root node
@@ -327,6 +339,16 @@ public class PathEntry extends ImportOptions {
}
}
+ // expand directive
+ this.requireContentReaders = new ArrayList<>();
+ final String requireContentReadersValue =
entry.getDirectiveValue(REQUIRE_CONTENT_READERS_DIRECTIVE);
+ if ( requireContentReadersValue != null &&
requireContentReadersValue.length() > 0 ) {
+ final StringTokenizer st = new
StringTokenizer(requireContentReadersValue, ",");
+ while ( st.hasMoreTokens() ) {
+ this.requireContentReaders.add(st.nextToken());
+ }
+ }
+
// workspace directive
final String workspaceValue =
entry.getDirectiveValue(WORKSPACE_DIRECTIVE);
if (pathValue != null) {
@@ -392,6 +414,42 @@ public class PathEntry extends ImportOptions {
return new HashSet<>(ignoreContentReaders);
}
+ @Override
+ public boolean isImportProviderRequired(@NotNull String name) {
+ boolean required = false;
+
+ if (!this.requireContentReaders.isEmpty()) {
+ // a directive was supplied, so use a filter to check if the
+ // name ends with the suffix and is not listed in the ignored
+ // import provider set
+ required = this.requireContentReaders.stream()
+ .anyMatch(suffix ->
+ // verify the file suffix matches
+ hasNameSuffix(name, suffix) &&
+ // and not one of the ignored providers
+ !isIgnoredImportProvider(suffix));
+ }
+ return required;
+ }
+
+ /**
+ * Check if the name ends with the supplied suffix
+ *
+ * @param name the name to check
+ * @param suffix the suffix to check
+ * @return true if the name ends with the suffix
+ */
+ private boolean hasNameSuffix(String name, String suffix) {
+ // ensure neither arg is null
+ return name != null && suffix != null &&
+ // is longer than suffix
+ name.length() > suffix.length() &&
+ // ends with suffix
+ name.endsWith(suffix) &&
+ // dot before the suffix
+ '.' == name.charAt(name.length() - suffix.length() - 1);
+ }
+
public String getTarget() {
return target;
}
diff --git
a/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoader.java
b/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoader.java
index 71bd020..acfce12 100644
---
a/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoader.java
+++
b/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoader.java
@@ -104,10 +104,29 @@ public class BundleContentLoader extends BaseImportLoader
{
bundleHelper = null;
}
+ /**
+ * Retry loading bundles that have previously been delayed
+ * @param metadataSession the JCR Session for reading/writing metadata
+ */
+ public void retryDelayedBundles(final Session metadataSession) {
+ // handle delayed bundles, might help now
+ int currentSize = -1;
+ for (int i = delayedBundles.size(); i > 0 && currentSize !=
delayedBundles.size()
+ && !delayedBundles.isEmpty(); i--) {
+ for (Iterator<Bundle> di = delayedBundles.iterator();
di.hasNext();) {
+ Bundle delayed = di.next();
+ if (registerBundleInternal(metadataSession, delayed, true,
false)) {
+ di.remove();
+ }
+ }
+ currentSize = delayedBundles.size();
+ }
+ }
+
/**
* Register a bundle and install its content.
*
- * @param metadataSession the JCR Session for reading/writing metadat
+ * @param metadataSession the JCR Session for reading/writing metadata
* @param bundle the bundle to install
*/
public void registerBundle(final Session metadataSession, final Bundle
bundle, final boolean isUpdate) {
@@ -120,18 +139,7 @@ public class BundleContentLoader extends BaseImportLoader {
log.debug("Registering bundle {} for content loading.",
bundle.getSymbolicName());
if (registerBundleInternal(metadataSession, bundle, false, isUpdate)) {
- // handle delayed bundles, might help now
- int currentSize = -1;
- for (int i = delayedBundles.size(); i > 0 && currentSize !=
delayedBundles.size()
- && !delayedBundles.isEmpty(); i--) {
- for (Iterator<Bundle> di = delayedBundles.iterator();
di.hasNext();) {
- Bundle delayed = di.next();
- if (registerBundleInternal(metadataSession, delayed, true,
false)) {
- di.remove();
- }
- }
- currentSize = delayedBundles.size();
- }
+ retryDelayedBundles(metadataSession);
} else if (!isUpdate) {
// add to delayed bundles - if this is not an update!
delayedBundles.add(bundle);
@@ -188,6 +196,12 @@ public class BundleContentLoader extends BaseImportLoader {
bundleHelper.unlockBundleContentInfo(metadataSession, bundle,
success, createdNodes);
}
+ } catch (ContentReaderUnavailableException crue) {
+ // if we are retrying we already logged this message once, so we
+ // won't log it again
+ if (!isRetry) {
+ log.warn("Cannot load initial content for bundle {} : {}",
bundle.getSymbolicName(), crue.getMessage());
+ }
} catch (RepositoryException re) {
// if we are retrying we already logged this message once, so we
// won't log it again
@@ -244,7 +258,7 @@ public class BundleContentLoader extends BaseImportLoader {
* @return If the content should be removed on uninstall, a list of top
nodes
*/
private List<String> installContent(final Session defaultSession, final
Bundle bundle,
- final Iterator<PathEntry> pathIter, final boolean
contentAlreadyLoaded) throws RepositoryException {
+ final Iterator<PathEntry> pathIter, final boolean
contentAlreadyLoaded) throws RepositoryException,
ContentReaderUnavailableException {
final List<String> createdNodes = new ArrayList<>();
final Map<String, Session> createdSessions = new HashMap<>();
@@ -352,7 +366,7 @@ public class BundleContentLoader extends BaseImportLoader {
*/
private void installFromPath(final Bundle bundle, final String path, final
PathEntry configuration,
final Node parent, final List<String> createdNodes, final
DefaultContentCreator contentCreator)
- throws RepositoryException {
+ throws RepositoryException, ContentReaderUnavailableException {
// init content creator
contentCreator.init(configuration, getContentReaders(), createdNodes,
null);
@@ -434,7 +448,7 @@ public class BundleContentLoader extends BaseImportLoader {
*/
private void handleFile(final String entry, final Bundle bundle, final
Map<String, Node> processedEntries,
final PathEntry configuration, final Node parent, final
List<String> createdNodes,
- final DefaultContentCreator contentCreator) throws
RepositoryException {
+ final DefaultContentCreator contentCreator) throws
RepositoryException, ContentReaderUnavailableException {
final URL file = bundle.getEntry(entry);
final String name = getName(entry);
@@ -466,7 +480,14 @@ public class BundleContentLoader extends BaseImportLoader {
log.warn("No node created for file {} {}", file, name);
}
} else {
- log.debug("Can't find content reader for entry {} at {}",
entry, name);
+ // if we require a ContentReader for this entry but didn't
find one
+ // then throw an exception to stop processing this bundle
and put
+ // it into the delayedBundles list to retry later
+ if (configuration.isImportProviderRequired(name)) {
+ throw new
ContentReaderUnavailableException(String.format("Unable to locate a required
content reader for entry %s", entry));
+ } else {
+ log.debug("Can't find content reader for entry {} at {}",
entry, name);
+ }
}
// otherwise just place as file
diff --git
a/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderListener.java
b/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderListener.java
index f00f231..09dfff0 100644
---
a/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderListener.java
+++
b/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderListener.java
@@ -64,7 +64,7 @@ import org.slf4j.LoggerFactory;
Constants.SERVICE_DESCRIPTION
+ "=Apache Sling Content Loader Implementation" }, immediate =
true, configurationPolicy = ConfigurationPolicy.OPTIONAL)
@Designate(ocd = BundleContentLoaderConfiguration.class, factory = false)
-public class BundleContentLoaderListener implements SynchronousBundleListener,
BundleHelper {
+public class BundleContentLoaderListener implements SynchronousBundleListener,
BundleHelper, ContentReaderWhiteboardListener {
public static final String PROPERTY_CONTENT_LOADED = "content-loaded";
public static final String PROPERTY_CONTENT_LOADED_AT =
"content-load-time";
@@ -133,6 +133,26 @@ public class BundleContentLoaderListener implements
SynchronousBundleListener, B
@Reference(target =
"(component.name=org.apache.sling.jcr.contentloader.internal.readers.ZipReader)")
private ContentReader mandatoryContentReader4;
+ // ---------- ContentReaderWhiteboardListener
-----------------------------------------------
+
+ /**
+ * When a new ContentReader component arrives, try to re-process any
+ * delayed bundles in case the new ContentReader makes it possible to
+ * process them now
+ */
+ @Override
+ public synchronized void handleContentReaderAdded(ContentReader operation)
{
+ Session session = null;
+ try {
+ session = this.getSession();
+ bundleContentLoader.retryDelayedBundles(session);
+ } catch (Exception t) {
+ log.error("handleContentReaderAdded: Problem loading initial
content of delayed bundles", t);
+ } finally {
+ this.ungetSession(session);
+ }
+ }
+
// ---------- BundleListener
-----------------------------------------------
/**
@@ -240,6 +260,8 @@ public class BundleContentLoaderListener implements
SynchronousBundleListener, B
this.bundleContentLoader = new BundleContentLoader(this,
contentReaderWhiteboard, configuration);
bundleContext.addBundleListener(this);
+ // start listening for new ContentReader components
+ contentReaderWhiteboard.setListener(this);
Session session = null;
try {
@@ -289,6 +311,8 @@ public class BundleContentLoaderListener implements
SynchronousBundleListener, B
@Deactivate
protected synchronized void deactivate(BundleContext bundleContext) {
bundleContext.removeBundleListener(this);
+ // stop listening for new ContentReader components
+ contentReaderWhiteboard.removeListener();
if (this.bundleContentLoader != null) {
this.bundleContentLoader.dispose();
diff --git
a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReaderUnavailableException.java
b/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReaderUnavailableException.java
new file mode 100644
index 0000000..0396d8e
--- /dev/null
+++
b/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReaderUnavailableException.java
@@ -0,0 +1,32 @@
+/*
+ * 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.jcr.contentloader.internal;
+
+/**
+ * This exception is thrown when a required ContentReader is not yet
+ * available.
+ */
+class ContentReaderUnavailableException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public ContentReaderUnavailableException(String message) {
+ super(message);
+ }
+
+}
diff --git
a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReaderWhiteboard.java
b/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReaderWhiteboard.java
index 3c6062e..0cbbac5 100644
---
a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReaderWhiteboard.java
+++
b/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReaderWhiteboard.java
@@ -30,10 +30,19 @@ import
org.osgi.service.component.annotations.ReferencePolicy;
})
public class ContentReaderWhiteboard {
+ private ContentReaderWhiteboardListener listener;
+
private Map<String, ContentReader> readersByExtension = new
LinkedHashMap<>();
private Map<String, ContentReader> readersByType = new LinkedHashMap<>();
+ public void setListener(ContentReaderWhiteboardListener listener) {
+ this.listener = listener;
+ }
+ public void removeListener() {
+ setListener(null);
+ }
+
public Map<String, ContentReader> getReadersByExtension() {
return readersByExtension;
}
@@ -61,6 +70,12 @@ public class ContentReaderWhiteboard {
}
}
}
+
+ // notify the listener that we have a new content reader
+ ContentReaderWhiteboardListener l = this.listener;
+ if (l != null) {
+ l.handleContentReaderAdded(operation);
+ }
}
protected void unbindContentReader(final Map<String, Object> properties) {
@@ -81,4 +96,4 @@ public class ContentReaderWhiteboard {
}
}
}
-}
\ No newline at end of file
+}
diff --git
a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReaderWhiteboardListener.java
b/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReaderWhiteboardListener.java
new file mode 100644
index 0000000..f572501
--- /dev/null
+++
b/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReaderWhiteboardListener.java
@@ -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.
+ */
+
+package org.apache.sling.jcr.contentloader.internal;
+
+import org.apache.sling.jcr.contentloader.ContentReader;
+
+/**
+ * Callback interface to allow the BundleContentLoaderListener to do work
+ * when a new ContentReader component arrives
+ */
+public interface ContentReaderWhiteboardListener {
+
+ void handleContentReaderAdded(final ContentReader operation);
+
+}
diff --git
a/src/test/java/org/apache/sling/jcr/contentloader/ImportOptionsFactory.java
b/src/test/java/org/apache/sling/jcr/contentloader/ImportOptionsFactory.java
index c1ab108..9708dcb 100644
--- a/src/test/java/org/apache/sling/jcr/contentloader/ImportOptionsFactory.java
+++ b/src/test/java/org/apache/sling/jcr/contentloader/ImportOptionsFactory.java
@@ -16,6 +16,8 @@
*/
package org.apache.sling.jcr.contentloader;
+import org.jetbrains.annotations.NotNull;
+
public final class ImportOptionsFactory {
public static final int NO_OPTIONS = 0;
@@ -33,8 +35,9 @@ public final class ImportOptionsFactory {
public static final int IGNORE_IMPORT_PROVIDER = 0x1 << 5;
public static final int CHECK_IN = 0x1 << 6;
-
-
+
+ public static final int REQUIRE_IMPORT_PROVIDER = 0x1 << 7;
+
public static ImportOptions createImportOptions(int options){
return new ImportOptions() {
@Override
@@ -62,6 +65,11 @@ public final class ImportOptionsFactory {
return (options & IGNORE_IMPORT_PROVIDER) > NO_OPTIONS;
}
+ @Override
+ public boolean isImportProviderRequired(@NotNull String name) {
+ return (options & REQUIRE_IMPORT_PROVIDER) > NO_OPTIONS;
+ }
+
@Override
public boolean isPropertyMerge() {
return (options & SYNCH_PROPERTIES) > NO_OPTIONS;
diff --git
a/src/test/java/org/apache/sling/jcr/contentloader/internal/SLING11203Test.java
b/src/test/java/org/apache/sling/jcr/contentloader/internal/SLING11203Test.java
new file mode 100644
index 0000000..d06d281
--- /dev/null
+++
b/src/test/java/org/apache/sling/jcr/contentloader/internal/SLING11203Test.java
@@ -0,0 +1,170 @@
+/*
+ * 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.jcr.contentloader.internal;
+
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.lang.reflect.Field;
+import java.util.Collections;
+
+import javax.jcr.Session;
+
+import org.apache.sling.jcr.contentloader.ContentReader;
+import org.apache.sling.jcr.contentloader.internal.readers.JsonReader;
+import org.apache.sling.jcr.contentloader.internal.readers.OrderedJsonReader;
+import org.apache.sling.jcr.contentloader.internal.readers.XmlReader;
+import org.apache.sling.jcr.contentloader.internal.readers.ZipReader;
+import org.apache.sling.testing.mock.sling.ResourceResolverType;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.osgi.framework.Bundle;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Testing content loader waiting for required content reader
+ */
+public class SLING11203Test {
+
+ protected org.slf4j.Logger logger = LoggerFactory.getLogger(getClass());
+
+ @Rule
+ public final SlingContext context = new
SlingContext(ResourceResolverType.JCR_OAK);
+
+ private BundleContentLoaderListener bundleHelper;
+
+ @Rule
+ public TestRule watcher = new TestWatcher() {
+
+ /* (non-Javadoc)
+ * @see
org.junit.rules.TestWatcher#starting(org.junit.runner.Description)
+ */
+ @Override
+ protected void starting(Description description) {
+ logger.info("Starting test: {}", description.getMethodName());
+ }
+
+ /* (non-Javadoc)
+ * @see
org.junit.rules.TestWatcher#finished(org.junit.runner.Description)
+ */
+ @Override
+ protected void finished(Description description) {
+ logger.info("Finished test: {}", description.getMethodName());
+ }
+
+ };
+
+ @Before
+ public void prepareContentLoader() throws Exception {
+ // NOTE: initially only the default set of content readers are
registered
+ context.registerInjectActivateService(JsonReader.class);
+ context.registerInjectActivateService(OrderedJsonReader.class);
+ context.registerInjectActivateService(XmlReader.class);
+ context.registerInjectActivateService(ZipReader.class);
+
+ // whiteboard which holds readers
+ context.registerInjectActivateService(new ContentReaderWhiteboard());
+
+ // register the content loader service
+ bundleHelper = context.registerInjectActivateService(new
BundleContentLoaderListener());
+
+ }
+
+ @Test
+ public void loadContentWithoutDirectiveExpectedContentReaderRegistered()
throws Exception {
+ loadContentWithDirective();
+
+ // check node was not added during parsing the file
+ assertThat("Included resource should not have been imported",
context.resourceResolver().getResource("/libs/app"), nullValue());
+ // check file was not loaded as non-parsed file
+ assertThat("Included resource should not have been imported",
context.resourceResolver().getResource("/libs/app.sling11203"), nullValue());
+ }
+
+ @Test
+ public void
loadContentWithDirectiveExpectedContentReaderRegisteredBeforeBundleLoaded()
throws Exception {
+ // register the content reader that we require before registering the
bundle
+ registerCustomContentReader();
+
+ loadContentWithDirective();
+
+ // check node was added during parsing the file
+ assertThat("Included resource should have been imported",
context.resourceResolver().getResource("/libs/app"), notNullValue());
+ // check file was not loaded as non-parsed file
+ assertThat("Included resource should not have been imported",
context.resourceResolver().getResource("/libs/app.sling11203"), nullValue());
+ }
+
+ @Test
+ public void
loadContentWithDirectiveExpectedContentReaderRegisteredAfterBundleLoaded()
throws Exception {
+ loadContentWithDirective();
+
+ // check node was not added during parsing the file
+ assertThat("Included resource should not have been imported",
context.resourceResolver().getResource("/libs/app"), nullValue());
+ // check file was not loaded as non-parsed file
+ assertThat("Included resource should not have been imported",
context.resourceResolver().getResource("/libs/app.sling11203"), nullValue());
+
+ // register the content reader that we require
+ registerCustomContentReader();
+
+ // check node was added during parsing the file
+ assertThat("Included resource should have been imported",
context.resourceResolver().getResource("/libs/app"), notNullValue());
+ // check file was not loaded as non-parsed file
+ assertThat("Included resource should not have been imported",
context.resourceResolver().getResource("/libs/app.sling11203"), nullValue());
+ }
+
+ protected void registerCustomContentReader() {
+ // register the content reader that we require after registering the
bundle
+ // to trigger the retry
+ context.registerService(ContentReader.class, new
SLING11203XmlReader(),
+ Collections.singletonMap(ContentReader.PROPERTY_EXTENSIONS,
"sling11203"));
+ }
+
+ protected void loadContentWithDirective() throws Exception {
+ // dig the BundleContentLoader out of the component field so we get the
+ // same instance so the state for the retry logic is there
+ Field privateBundleContentLoaderField =
BundleContentLoaderListener.class.getDeclaredField("bundleContentLoader");
+ privateBundleContentLoaderField.setAccessible(true);
+ BundleContentLoader contentLoader =
(BundleContentLoader)privateBundleContentLoaderField.get(bundleHelper);
+
+ // requireImportProviders directive, so it should check if the
specified
+ // required content reader is available
+ String initialContentHeader =
"SLING-INF3/libs;path:=/libs;requireImportProviders:=sling11203";
+ Bundle mockBundle =
BundleContentLoaderTest.newBundleWithInitialContent(context,
initialContentHeader);
+
+
contentLoader.registerBundle(context.resourceResolver().adaptTo(Session.class),
mockBundle, false);
+ }
+
+ /**
+ * A custom xml reader with a different file extension
+ */
+ public static class SLING11203XmlReader extends XmlReader {
+
+ private SLING11203XmlReader() {
+ super();
+ activate();
+ }
+
+ }
+
+}
diff --git a/src/test/resources/SLING-INF3/libs/app.sling11203
b/src/test/resources/SLING-INF3/libs/app.sling11203
new file mode 100644
index 0000000..b8d80c6
--- /dev/null
+++ b/src/test/resources/SLING-INF3/libs/app.sling11203
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<node>
+ <primaryNodeType>sling:Folder</primaryNodeType>
+</node>