| /* |
| * 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.loader; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.Dictionary; |
| import java.util.EnumSet; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.function.Function; |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| |
| import org.apache.commons.io.IOUtils; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.jackrabbit.JcrConstants; |
| import org.apache.sling.api.resource.PersistenceException; |
| import org.apache.sling.api.resource.Resource; |
| import org.apache.sling.api.resource.ResourceResolver; |
| import org.apache.sling.api.resource.ResourceResolverFactory; |
| import org.apache.sling.api.resource.ResourceUtil; |
| import org.apache.sling.commons.mime.MimeTypeService; |
| import org.apache.sling.contentparser.api.ContentParser; |
| import org.apache.sling.contentparser.api.ParserOptions; |
| import org.apache.sling.contentparser.json.JSONParserFeature; |
| import org.apache.sling.contentparser.json.JSONParserOptions; |
| import org.apache.sling.contentparser.json.internal.JSONContentParser; |
| import org.apache.sling.contentparser.xml.jcr.internal.JCRXMLContentParser; |
| import org.apache.sling.fsprovider.internal.FsResourceProvider; |
| import org.apache.sling.spi.resource.provider.ResourceProvider; |
| import org.apache.sling.testing.mock.osgi.MapUtil; |
| import org.apache.sling.testing.mock.osgi.MockOsgi; |
| import org.apache.sling.testing.mock.sling.ResourceResolverType; |
| import org.apache.sling.testing.mock.sling.builder.ImmutableValueMap; |
| import org.apache.sling.testing.resourceresolver.MockResourceResolverFactory; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.ServiceReference; |
| |
| /** |
| * Imports JSON data and binary data into Sling resource hierarchy. |
| * After all import operations from json or binaries {@link ResourceResolver#commit()} is called (when autocommit mode is active). |
| */ |
| public final class ContentLoader { |
| |
| private static final String CONTENTTYPE_OCTET_STREAM = "application/octet-stream"; |
| |
| // set of resource or property names that are ignored for all resource resolver types |
| private static final Set<String> SHARED_IGNORED_NAMES = Stream.of( |
| JcrConstants.JCR_BASEVERSION, |
| JcrConstants.JCR_PREDECESSORS, |
| JcrConstants.JCR_SUCCESSORS, |
| JcrConstants.JCR_VERSIONHISTORY, |
| "jcr:checkedOut", |
| "jcr:isCheckedOut", |
| "rep:policy") |
| .collect(Collectors.toSet()); |
| |
| // set of resource or property names that are ignored when other resource resolver types than JCR_OAK are used |
| private static final Set<String> MOCK_IGNORED_NAMES = Stream.concat( |
| SHARED_IGNORED_NAMES.stream(), Stream.of( |
| JcrConstants.JCR_MIXINTYPES)) |
| .collect(Collectors.toSet()); |
| |
| // set of resource or property names that are ignored when JCR_OAK resource resolver type (= a real repo impl) is used |
| private static final Set<String> OAK_IGNORED_NAMES = Stream.concat( |
| SHARED_IGNORED_NAMES.stream(), Stream.of( |
| JcrConstants.JCR_UUID, |
| JcrConstants.JCR_CREATED)) |
| .collect(Collectors.toSet()); |
| |
| private final ResourceResolver resourceResolver; |
| private final BundleContext bundleContext; |
| private final boolean autoCommit; |
| private final Set<String> ignoredNames; |
| private final ContentParser jsonParser; |
| private final ParserOptions jsonParserOptions; |
| private final ContentParser fileVaultXmlParser; |
| private final ParserOptions fileVaultXmlParserOptions; |
| |
| /** |
| * @param resourceResolver Resource resolver |
| */ |
| public ContentLoader(@NotNull ResourceResolver resourceResolver) { |
| this(resourceResolver, null); |
| } |
| |
| /** |
| * @param resourceResolver Resource resolver |
| * @param bundleContext Bundle context |
| */ |
| public ContentLoader(@NotNull ResourceResolver resourceResolver, @Nullable BundleContext bundleContext) { |
| this(resourceResolver, bundleContext, true); |
| } |
| |
| /** |
| * @param resourceResolver Resource resolver |
| * @param bundleContext Bundle context |
| * @param autoCommit Automatically commit changes after loading content (default: true) |
| */ |
| public ContentLoader(@NotNull ResourceResolver resourceResolver, @Nullable BundleContext bundleContext, boolean autoCommit) { |
| this(resourceResolver, bundleContext, autoCommit, null); |
| } |
| |
| /** |
| * @param resourceResolver Resource resolver |
| * @param bundleContext Bundle context |
| * @param autoCommit Automatically commit changes after loading content (default: true) |
| * @param resourceResolverType Resource resolver type. |
| */ |
| public ContentLoader(@NotNull ResourceResolver resourceResolver, @Nullable BundleContext bundleContext, boolean autoCommit, |
| @Nullable ResourceResolverType resourceResolverType) { |
| this.resourceResolver = resourceResolver; |
| this.bundleContext = bundleContext; |
| this.autoCommit = autoCommit; |
| this.ignoredNames = getIgnoredNamesForResourceResolverType(resourceResolverType); |
| |
| this.jsonParserOptions = new JSONParserOptions() |
| .withFeatures(EnumSet.of(JSONParserFeature.COMMENTS, JSONParserFeature.QUOTE_TICK)) |
| .detectCalendarValues(true) |
| .ignorePropertyNames(this.ignoredNames) |
| .ignoreResourceNames(this.ignoredNames); |
| this.jsonParser = new JSONContentParser(); |
| |
| this.fileVaultXmlParserOptions = new ParserOptions() |
| .detectCalendarValues(true) |
| .ignorePropertyNames(this.ignoredNames) |
| .ignoreResourceNames(this.ignoredNames); |
| this.fileVaultXmlParser = new JCRXMLContentParser(); |
| } |
| |
| private final Set<String> getIgnoredNamesForResourceResolverType(ResourceResolverType resourceResolverType) { |
| if (resourceResolverType == null || resourceResolverType == ResourceResolverType.JCR_OAK) { |
| return OAK_IGNORED_NAMES; |
| } |
| else { |
| return MOCK_IGNORED_NAMES; |
| } |
| } |
| |
| /** |
| * Import content of JSON file into repository. |
| * <ul> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param classpathResourceOrFile Classpath resource URL or file path for JSON content |
| * @param parentResource Parent resource |
| * @param childName Name of child resource to create with JSON content |
| * @return Resource |
| */ |
| public @NotNull Resource json(@NotNull String classpathResourceOrFile, @NotNull Resource parentResource, @NotNull String childName) { |
| return json(classpathResourceOrFile, parentResource.getPath() + "/" + childName); |
| } |
| |
| /** |
| * Import content of JSON file into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param classpathResourceOrFile Classpath resource URL or file path for JSON content |
| * @param destPath Path to import the JSON content to |
| * @return Resource |
| */ |
| public @NotNull Resource json(@NotNull String classpathResourceOrFile, @NotNull String destPath) { |
| return processInputStreamFromClasspathOrFilesystem(classpathResourceOrFile, is -> json(is, destPath)); |
| } |
| |
| /** |
| * Import content of JSON file into repository. |
| * <ul> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param inputStream Input stream with JSON content |
| * @param parentResource Parent resource |
| * @param childName Name of child resource to create with JSON content |
| * @return Resource |
| */ |
| public @NotNull Resource json(@NotNull InputStream inputStream, @NotNull Resource parentResource, @NotNull String childName) { |
| return json(inputStream, parentResource.getPath() + "/" + childName); |
| } |
| |
| /** |
| * Import content of JSON file into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param inputStream Input stream with JSON content |
| * @param destPath Path to import the JSON content to |
| * @return Resource |
| */ |
| public @NotNull Resource json(@NotNull InputStream inputStream, @NotNull String destPath) { |
| return mountParsedFile(inputStream, destPath, jsonParser, jsonParserOptions); |
| } |
| |
| /** |
| * Import content of FileVault XML file into repository. |
| * <ul> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param classpathResourceOrFile Classpath resource URL or file path to single FileVault XML file (usually <code>.content.xml</code>) |
| * @param parentResource Parent resource |
| * @param childName Name of child resource to create with Filevault content |
| * @return Resource |
| */ |
| public @NotNull Resource fileVaultXml(@NotNull String classpathResourceOrFile, @NotNull Resource parentResource, @NotNull String childName) { |
| return fileVaultXml(classpathResourceOrFile, parentResource.getPath() + "/" + childName); |
| } |
| |
| /** |
| * Import content of FileVault XML file into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param classpathResourceOrFile Classpath resource URL or file path to single FileVault XML file (usually <code>.content.xml</code>) |
| * @param destPath Path to import the Filevault content to |
| * @return Resource |
| */ |
| public @NotNull Resource fileVaultXml(@NotNull String classpathResourceOrFile, @NotNull String destPath) { |
| return processInputStreamFromClasspathOrFilesystem(classpathResourceOrFile, is -> fileVaultXml(is, destPath)); |
| } |
| |
| /** |
| * Import content of FileVault XML file into repository. |
| * <ul> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param inputStream Input stream with Filevault content |
| * @param parentResource Parent resource |
| * @param childName Name of child resource to create with Filevault content |
| * @return Resource |
| */ |
| public @NotNull Resource fileVaultXml(@NotNull InputStream inputStream, @NotNull Resource parentResource, @NotNull String childName) { |
| return fileVaultXml(inputStream, parentResource.getPath() + "/" + childName); |
| } |
| |
| /** |
| * Import content of FileVault XML file into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param inputStream Input stream with Filevault content |
| * @param destPath Path to import the Filevault content to |
| * @return Resource |
| */ |
| public @NotNull Resource fileVaultXml(@NotNull InputStream inputStream, @NotNull String destPath) { |
| return mountParsedFile(inputStream, destPath, fileVaultXmlParser, fileVaultXmlParserOptions); |
| } |
| |
| @SuppressWarnings("null") |
| private @NotNull Resource mountParsedFile(@NotNull InputStream inputStream, @NotNull String destPath, |
| @NotNull ContentParser contentParser, @NotNull ParserOptions parserOptions) { |
| try { |
| String parentPath = ResourceUtil.getParent(destPath); |
| String childName = ResourceUtil.getName(destPath); |
| |
| if (parentPath == null) { |
| throw new IllegalArgumentException("Path has no parent: " + destPath); |
| } |
| |
| Resource parentResource = resourceResolver.getResource(parentPath); |
| if (parentResource == null) { |
| parentResource = createResourceHierarchy(parentPath); |
| } |
| if (parentResource.getChild(childName) != null) { |
| throw new IllegalArgumentException("Resource does already exist: " + destPath); |
| } |
| |
| LoaderContentHandler contentHandler = new LoaderContentHandler(destPath, resourceResolver); |
| contentParser.parse(contentHandler, inputStream, parserOptions); |
| if (autoCommit) { |
| resourceResolver.commit(); |
| } |
| return resourceResolver.getResource(destPath); |
| } |
| catch (IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| private @NotNull Resource createResourceHierarchy(@NotNull String path) { |
| String parentPath = ResourceUtil.getParent(path); |
| if (parentPath == null) { |
| throw new IllegalArgumentException("Path has no parent: " + path); |
| } |
| Resource parentResource = resourceResolver.getResource(parentPath); |
| if (parentResource == null) { |
| parentResource = createResourceHierarchy(parentPath); |
| } |
| Map<String, Object> props = new HashMap<String, Object>(); |
| props.put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED); |
| try { |
| return resourceResolver.create(parentResource, ResourceUtil.getName(path), props); |
| } catch (PersistenceException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| /** |
| * Import binary file as nt:file binary node into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>Mime type is auto-detected from either {@code classpathResourceOrFile} or {@code path}.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param classpathResourceOrFile Classpath resource URL or file path for binary file. |
| * @param path Path to mount binary data to (parent nodes created |
| * automatically) |
| * @return Resource with binary data |
| */ |
| public @NotNull Resource binaryFile(@NotNull String classpathResourceOrFile, @NotNull String path) { |
| return binaryFile(classpathResourceOrFile, path, detectMimeTypeFromNames(classpathResourceOrFile, path)); |
| } |
| |
| /** |
| * Import binary file as nt:file binary node into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param classpathResourceOrFile Classpath resource URL or file path for binary file. |
| * @param path Path to mount binary data to (parent nodes created |
| * automatically) |
| * @param mimeType Mime type of binary data |
| * @return Resource with binary data |
| */ |
| public @NotNull Resource binaryFile(@NotNull String classpathResourceOrFile, @NotNull String path, @NotNull String mimeType) { |
| return processInputStreamFromClasspathOrFilesystem(classpathResourceOrFile, is -> binaryFile(is, path, mimeType)); |
| } |
| |
| /** |
| * Import binary file as nt:file binary node into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>Mime type is auto-detected from resource name.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param inputStream Input stream for binary data |
| * @param path Path to mount binary data to (parent nodes created |
| * automatically) |
| * @return Resource with binary data |
| */ |
| public @NotNull Resource binaryFile(@NotNull InputStream inputStream, @NotNull String path) { |
| return binaryFile(inputStream, path, detectMimeTypeFromNames(path)); |
| } |
| |
| /** |
| * Import binary file as nt:file binary node into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param inputStream Input stream for binary data |
| * @param path Path to mount binary data to (parent nodes created |
| * automatically) |
| * @param mimeType Mime type of binary data |
| * @return Resource with binary data |
| */ |
| public @NotNull Resource binaryFile(@NotNull InputStream inputStream, @NotNull String path, @NotNull String mimeType) { |
| String parentPath = ResourceUtil.getParent(path, 1); |
| String name = ResourceUtil.getName(path); |
| if (parentPath == null) { |
| throw new IllegalArgumentException("Path has no parent: " + path); |
| } |
| Resource parentResource = resourceResolver.getResource(parentPath); |
| if (parentResource == null) { |
| parentResource = createResourceHierarchy(parentPath); |
| } |
| return binaryFile(inputStream, parentResource, name, mimeType); |
| } |
| |
| /** |
| * Import binary file as nt:file binary node into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>Mime type is auto-detected from resource name.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param inputStream Input stream for binary data |
| * @param parentResource Parent resource |
| * @param name Resource name for nt:file |
| * @return Resource with binary data |
| */ |
| public @NotNull Resource binaryFile(@NotNull InputStream inputStream, @NotNull Resource parentResource, @NotNull String name) { |
| return binaryFile(inputStream, parentResource, name, detectMimeTypeFromNames(name)); |
| } |
| |
| /** |
| * Import binary file as nt:file binary node into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param inputStream Input stream for binary data |
| * @param parentResource Parent resource |
| * @param name Resource name for nt:file |
| * @param mimeType Mime type of binary data |
| * @return Resource with binary data |
| */ |
| public @NotNull Resource binaryFile(@NotNull InputStream inputStream, @NotNull Resource parentResource, @NotNull String name, @NotNull String mimeType) { |
| try { |
| Resource file = resourceResolver.create(parentResource, name, |
| ImmutableValueMap.of(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_FILE)); |
| resourceResolver.create(file, JcrConstants.JCR_CONTENT, |
| ImmutableValueMap.of(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_RESOURCE, |
| JcrConstants.JCR_DATA, inputStream, |
| JcrConstants.JCR_MIMETYPE, mimeType)); |
| if (autoCommit) { |
| resourceResolver.commit(); |
| } |
| return file; |
| } catch (PersistenceException ex) { |
| throw new RuntimeException("Unable to create resource at " + parentResource.getPath() + "/" + name, ex); |
| } |
| } |
| |
| /** |
| * Import binary file as nt:resource binary node into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>Mime type is auto-detected from {@code classpathResourceOrFile} or {@code path}.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param classpathResourceOrFile Classpath resource URL or file path for binary file. |
| * @param path Path to mount binary data to (parent nodes created automatically) |
| * @return Resource with binary data |
| */ |
| public @NotNull Resource binaryResource(@NotNull String classpathResourceOrFile, @NotNull String path) { |
| return binaryResource(classpathResourceOrFile, path, detectMimeTypeFromNames(classpathResourceOrFile, path)); |
| } |
| |
| /** |
| * Import binary file as nt:resource binary node into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param classpathResourceOrFile Classpath resource URL or file path for binary file. |
| * @param path Path to mount binary data to (parent nodes created automatically) |
| * @param mimeType Mime type of binary data |
| * @return Resource with binary data |
| */ |
| public @NotNull Resource binaryResource(@NotNull String classpathResourceOrFile, @NotNull String path, @NotNull String mimeType) { |
| return processInputStreamFromClasspathOrFilesystem(classpathResourceOrFile, is -> binaryResource(is, path, mimeType)); |
| } |
| |
| /** |
| * Import binary file as nt:resource binary node into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>Mime type is auto-detected from resource name.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param inputStream Input stream for binary data |
| * @param path Path to mount binary data to (parent nodes created |
| * automatically) |
| * @return Resource with binary data |
| */ |
| public @NotNull Resource binaryResource(@NotNull InputStream inputStream, @NotNull String path) { |
| return binaryResource(inputStream, path, detectMimeTypeFromNames(path)); |
| } |
| |
| /** |
| * Import binary file as nt:resource binary node into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param inputStream Input stream for binary data |
| * @param path Path to mount binary data to (parent nodes created |
| * automatically) |
| * @param mimeType Mime type of binary data |
| * @return Resource with binary data |
| */ |
| public @NotNull Resource binaryResource(@NotNull InputStream inputStream, @NotNull String path, @NotNull String mimeType) { |
| String parentPath = ResourceUtil.getParent(path, 1); |
| String name = ResourceUtil.getName(path); |
| if (parentPath == null) { |
| throw new IllegalArgumentException("Path has no parent: " + path); |
| } |
| Resource parentResource = resourceResolver.getResource(parentPath); |
| if (parentResource == null) { |
| parentResource = createResourceHierarchy(parentPath); |
| } |
| return binaryResource(inputStream, parentResource, name, mimeType); |
| } |
| |
| /** |
| * Import binary file as nt:resource binary node into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>Mime type is auto-detected from resource name.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param inputStream Input stream for binary data |
| * @param parentResource Parent resource |
| * @param name Resource name for nt:resource |
| * @return Resource with binary data |
| */ |
| public @NotNull Resource binaryResource(@NotNull InputStream inputStream, @NotNull Resource parentResource, @NotNull String name) { |
| return binaryResource(inputStream, parentResource, name, detectMimeTypeFromNames(name)); |
| } |
| |
| /** |
| * Import binary file as nt:resource binary node into repository. |
| * <ul> |
| * <li>Auto-creates parent hierarchies as nt:unstrucured nodes if missing.</li> |
| * <li>The imported resources support reading and writing.</li> |
| * </ul> |
| * @param inputStream Input stream for binary data |
| * @param parentResource Parent resource |
| * @param name Resource name for nt:resource |
| * @param mimeType Mime type of binary data |
| * @return Resource with binary data |
| */ |
| public @NotNull Resource binaryResource(@NotNull InputStream inputStream, @NotNull Resource parentResource, @NotNull String name, @NotNull String mimeType) { |
| try { |
| Resource resource = resourceResolver.create(parentResource, name, |
| ImmutableValueMap.of(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_RESOURCE, |
| JcrConstants.JCR_DATA, inputStream, |
| JcrConstants.JCR_MIMETYPE, mimeType)); |
| if (autoCommit) { |
| resourceResolver.commit(); |
| } |
| return resource; |
| } catch (PersistenceException ex) { |
| throw new RuntimeException("Unable to create resource at " + parentResource.getPath() + "/" + name, ex); |
| } |
| } |
| |
| /** |
| * Detected mime type from any of the given names (evaluating the file extension) using Mime Type service. |
| * Fallback to application/octet-stream. |
| * @param names The names from which to derive the mime type |
| * @return Mime type (never null) |
| */ |
| @SuppressWarnings("null") |
| private @NotNull String detectMimeTypeFromNames(@NotNull String @NotNull ... names) { |
| String mimeType = null; |
| for (String name : names) { |
| String fileExtension = StringUtils.substringAfterLast(name, "."); |
| if (bundleContext != null && StringUtils.isNotEmpty(fileExtension)) { |
| ServiceReference<MimeTypeService> ref = bundleContext.getServiceReference(MimeTypeService.class); |
| if (ref != null) { |
| MimeTypeService mimeTypeService = bundleContext.getService(ref); |
| mimeType = mimeTypeService.getMimeType(fileExtension); |
| break; |
| } |
| } |
| } |
| return StringUtils.defaultString(mimeType, CONTENTTYPE_OCTET_STREAM); |
| } |
| |
| /** |
| * Mount a folder (file system) containing content in JSON (Sling-Initial-Content) format in repository. |
| * <ul> |
| * <li>The resources are not imported, but mounted via FS Resource Provider.</li> |
| * <li>The mounted resource tree is read-only.</li> |
| * </ul> |
| * @param mountFolderPath Root folder path to mount |
| * @param parentResource Parent resource |
| * @param childName Name of child resource to mount folder into |
| */ |
| public void folderJson(@NotNull String mountFolderPath, @NotNull Resource parentResource, @NotNull String childName) { |
| folderJson(new File(mountFolderPath), parentResource, childName); |
| } |
| |
| /** |
| * Mount a folder (file system) containing content in JSON (Sling-Initial-Content) format in repository. |
| * <ul> |
| * <li>The resources are not imported, but mounted via FS Resource Provider.</li> |
| * <li>The mounted resource tree is read-only.</li> |
| * </ul> |
| * @param mountFolderPath Root folder path to mount |
| * @param destPath Path to mount folder into |
| */ |
| public void folderJson(@NotNull String mountFolderPath, @NotNull String destPath) { |
| folderJson(new File(mountFolderPath), destPath); |
| } |
| |
| /** |
| * Mount a folder containing content in JSON (Sling-Initial-Content) format in repository. |
| * <ul> |
| * <li>The resources are not imported, but mounted via FS Resource Provider.</li> |
| * <li>The mounted resource tree is read-only.</li> |
| * </ul> |
| * @param mountFolder Root folder to mount |
| * @param parentResource Parent resource |
| * @param childName Name of child resource to mount folder into |
| */ |
| public void folderJson(@NotNull File mountFolder, @NotNull Resource parentResource, @NotNull String childName) { |
| folderJson(mountFolder, parentResource.getPath() + "/" + childName); |
| } |
| |
| /** |
| * Mount a folder containing content in JSON (Sling-Initial-Content) format in repository. |
| * <ul> |
| * <li>The resources are not imported, but mounted via FS Resource Provider.</li> |
| * <li>The mounted resource tree is read-only.</li> |
| * </ul> |
| * @param mountFolder Root folder to mount |
| * @param destPath Path to mount folder into |
| */ |
| public void folderJson(@NotNull File mountFolder, @NotNull String destPath) { |
| registerFileSystemResourceProvider( |
| "provider.file", mountFolder.getAbsolutePath(), |
| "provider.root", destPath, |
| "provider.fs.mode", "INITIAL_CONTENT", |
| "provider.initial.content.import.options", "overwrite:=true;ignoreImportProviders:=\"xml\"", |
| "provider.checkinterval", 0); |
| } |
| |
| /** |
| * Mount a folder (file system) containing content in FileVault XML format in repository. |
| * <ul> |
| * <li>The resources are not imported, but mounted via FS Resource Provider.</li> |
| * <li>The mounted resource tree is read-only.</li> |
| * </ul> |
| * @param mountFolderPath Root folder path to mount. Path needs to point to the root folder of the content package structure. |
| * @param parentResource Parent resource |
| * @param childName Name of child resource of subtree path that should be mounted from FileVault XML structure |
| */ |
| public void folderFileVaultXml(@NotNull String mountFolderPath, @NotNull Resource parentResource, @NotNull String childName) { |
| folderFileVaultXml(new File(mountFolderPath), parentResource, childName); |
| } |
| |
| /** |
| * Mount a folder (file system) containing content in FileVault XML format in repository. |
| * <ul> |
| * <li>The resources are not imported, but mounted via FS Resource Provider.</li> |
| * <li>The mounted resource tree is read-only.</li> |
| * </ul> |
| * @param mountFolderPath Root folder path to mount. Path needs to point to the root folder of the content package structure. |
| * @param destPath Subtree path that should be mounted from FileVault XML structure |
| */ |
| public void folderFileVaultXml(@NotNull String mountFolderPath, @NotNull String destPath) { |
| folderFileVaultXml(new File(mountFolderPath), destPath); |
| } |
| |
| /** |
| * Mount a folder containing content in FileVault XML format in repository. |
| * <ul> |
| * <li>The resources are not imported, but mounted via FS Resource Provider.</li> |
| * <li>The mounted resource tree is read-only.</li> |
| * </ul> |
| * @param mountFolder Root folder to mount. Path needs to point to the root folder of the content package structure. |
| * @param parentResource Parent resource |
| * @param childName Name of child resource of subtree path that should be mounted from FileVault XML structure |
| */ |
| public void folderFileVaultXml(@NotNull File mountFolder, @NotNull Resource parentResource, @NotNull String childName) { |
| folderFileVaultXml(mountFolder, parentResource.getPath() + "/" + childName); |
| } |
| |
| /** |
| * Mount a folder containing content in FileVault XML format in repository. |
| * <ul> |
| * <li>The resources are not imported, but mounted via FS Resource Provider.</li> |
| * <li>The mounted resource tree is read-only.</li> |
| * </ul> |
| * @param mountFolder Root folder to mount. Path needs to point to the root folder of the content package structure. |
| * @param destPath Subtree path that should be mounted from FileVault XML structure |
| */ |
| public void folderFileVaultXml(@NotNull File mountFolder, @NotNull String destPath) { |
| registerFileSystemResourceProvider( |
| "provider.file", mountFolder.getAbsolutePath(), |
| "provider.root", destPath, |
| "provider.fs.mode", "FILEVAULT_XML", |
| "provider.checkinterval", 0); |
| } |
| |
| /** |
| * Get input stream for a resource either from classpath (preferred) or from filesystem (fallback). |
| * @param classpathResourceOrFile Classpath resource URL or file path |
| * @param processor Processes input stream |
| */ |
| @SuppressWarnings({ "null", "deprecation" }) |
| private <T> @NotNull T processInputStreamFromClasspathOrFilesystem(@NotNull String classpathResourceOrFile, @NotNull Function<InputStream,T> processor) { |
| InputStream is = ContentLoader.class.getResourceAsStream(classpathResourceOrFile); |
| if (is == null) { |
| try { |
| is = new FileInputStream(classpathResourceOrFile); |
| } |
| catch (FileNotFoundException ex) { |
| throw new IllegalArgumentException("Classpath resource or file not found: " + classpathResourceOrFile); |
| } |
| } |
| try { |
| return processor.apply(is); |
| } |
| finally { |
| IOUtils.closeQuietly(is); |
| } |
| } |
| |
| @SuppressWarnings("null") |
| private void registerFileSystemResourceProvider(Object... serviceProperties) { |
| if (bundleContext == null) { |
| throw new IllegalStateException("No bundle context given for content loader."); |
| } |
| if (isUsingMockResourceResolverFactory()) { |
| throw new IllegalStateException("Loading folder content is not supported with RESOURCERESOLVER_MOCK resource resolver type. " |
| + "Use RESOURCEPROVIDER_MOCK or one of the other types."); |
| } |
| Dictionary<String,Object> props = MapUtil.toDictionary(serviceProperties); |
| FsResourceProvider service = MockOsgi.activateInjectServices(FsResourceProvider.class, bundleContext, props); |
| bundleContext.registerService(ResourceProvider.class, service, props); |
| } |
| |
| private boolean isUsingMockResourceResolverFactory() { |
| ServiceReference<ResourceResolverFactory> serviceReference = bundleContext.getServiceReference(ResourceResolverFactory.class); |
| if (serviceReference == null) { |
| throw new IllegalStateException("No resource resolver factory service present."); |
| } |
| try { |
| ResourceResolverFactory resourceResolverFactory = bundleContext.getService(serviceReference); |
| return resourceResolverFactory instanceof MockResourceResolverFactory; |
| } |
| finally { |
| bundleContext.ungetService(serviceReference); |
| } |
| } |
| |
| } |