Class SourceModel

  • All Implemented Interfaces:
    com.composum.sling.core.bean.RestrictedBean, com.composum.sling.core.SlingBean

    public class SourceModel
    extends ConsoleSlingBean

    Model that can produce XML source, ZIP files or Vault-packages for it's resource. This is quite similar to what Jackrabbit Vault produces, but we make a few differences, since our main intention here is to produce good source code for our site. These are:

    • This is based on Sling Resources and does not use JCR itself like Vault does. Thus, it could also be used for non-JCR based resources. (One exception is that we need JCR to determine whether a nodes children are orderable and to find out primaryType relationships, but we try to degrade gracefully if that fails.)
    • We do not export properties that change on each im- and export, since importing and exporting a site should not change it's source - compare EXCLUDED_PROPS. For instance, jcr:created* and jcr:lastModified* are omitted, as are the properties mix:versionable creates. Also, jcr:uuid is not exported, as it will change on each import and since we regard references to jcr:uuid as dangerous, to be used only sparingly if at all. (Composum uses absolute paths as references.)
    • In the .content.xml for a node with a jcr:content node, all subnodes of this node will be included, even if they happen to be a nt:folder. The only exception here are binary properties / files, which are written into separate files, since we don't want binary content in XML files.
    • If nt:file have additional properties, we do always create an "extended file aggregate" with an additional directory {filename}.dir to avoid having parts of the file hidden in the .content.xml, invisible in a file browser

    To summarize, we follow the following rules for our output.

    • Resources with a primary type of nt:folder (or subtypes) or with a subnode named jcr:content are exported as a folder with a .content.xml. Other nodes (barring files and binary properties) are exported in the next higher .content.xml.
    • Resources with type nt:file are exported as a file with a name according to their name; if they contain additional attributes an additional file {filename}_dir/.content.xml is created for them, which also contains other subnodes of the file node (barring files and binary attributes).
    • Resources with type nt:resource that are not below a nt:file are exported into a file named like the resource with an extension added according to the mime type.
    • Binary resources (except jcr:data below nt:file + nt:resource) are exported as a file {resource}/{ propertyname}.binary, with an entry {propertyname}="{BINARY}" in the .content.xml to declare the attribute with it's type.
    • If a node has orderable subnodes, it includes entries for all it's children - possibly empty nodes if they have their own folder / file according to the above rules.
    • Only "source compatible" properties are exported - we ignore protected properties and properties that change on import-export cycles. (See EXCLUDED_PROPS).

    Limitations:

    • Since we do not export jcr:uuid, references don't work.
    • This will, obviously, not be quite identical to an export with Jackrabbit Vault, though it is compatible to be imported into JCR via Vault.
    See Also:
    "https://jackrabbit.apache.org/filevault/vaultfs.html"
    • Field Detail

      • EXCLUDED_PROPS

        public static final com.composum.sling.core.filter.StringFilter EXCLUDED_PROPS
        Matches a number of properties that do not belong into a source.

        TODO move to configuration

      • EXCLUDED_MIXINS

        public static final com.composum.sling.core.filter.StringFilter EXCLUDED_MIXINS
        Matches mixins that do not belong into a source. E.g. rep:AccessControllable doesn't make sense since we do not export ACLs, anyway, and adding ACLs automatically adds this mixin.
      • BASIC_INDENT

        public static final String BASIC_INDENT
        Indentation level for one level in the XML hierarchy in an XML document.
        See Also:
        Constant Field Values
      • PATH_WITHIN_JCR_CONTENT

        protected static final Pattern PATH_WITHIN_JCR_CONTENT
      • subnodeList

        protected transient List<org.apache.sling.api.resource.Resource> subnodeList
      • hasOrderableSiblings

        protected transient Boolean[] hasOrderableSiblings
        Whether the siblings are known to be orderable - wrapped into array to distinguish "not known" from "not yet determined".
      • hasOrderableChildren

        protected transient Boolean[] hasOrderableChildren
        Whether the child nodes are known to be orderable - wrapped into array to distinguish "not known" from "not yet determined".
    • Constructor Detail

      • SourceModel

        public SourceModel​(NodesConfiguration config,
                           com.composum.sling.core.BeanContext context,
                           org.apache.sling.api.resource.Resource resource)
    • Method Detail

      • getExportRootPath

        @NotNull
        public @NotNull String getExportRootPath()
        Returns:
        the root path to use for exporting artifacts, honors a root path (mount poiunt) of an extended resolver
      • getExportPath

        @NotNull
        public @NotNull String getExportPath​(@NotNull
                                             @NotNull String path)
        Parameters:
        path - the path to map for exporting
        Returns:
        the path relative to the export root path
      • isRootPath

        public boolean isRootPath​(@NotNull
                                  @NotNull String aPath)
        Parameters:
        path - a resource path in the resource repository tree; maybe a mounted resource path
        Returns:
        'true' if the path is equal to the export root path
      • getName

        public String getName()
        Specified by:
        getName in interface com.composum.sling.core.SlingBean
        Overrides:
        getName in class com.composum.sling.core.AbstractSlingBean
      • getPrimaryType

        public String getPrimaryType()
      • getLastModified

        public FileTime getLastModified​(org.apache.sling.api.resource.Resource rawResource)
      • hasSubnodes

        public boolean hasSubnodes()
      • getSubnodeList

        public List<org.apache.sling.api.resource.Resource> getSubnodeList()
      • determineNamespaces

        protected void determineNamespaces​(List<String> keys,
                                           boolean inFullCoverage)
      • addNameNamespace

        protected void addNameNamespace​(List<String> keys,
                                        String aName)
      • getNamespace

        protected String getNamespace​(String aName)
      • hasOrderableSiblings

        public boolean hasOrderableSiblings()
        Returns true if the nodes siblings are ordered. Works only for JCR resources - if we cannot determine this, we return 'false'.
      • hasOrderableChildren

        public boolean hasOrderableChildren()
        Returns true if the nodes children are ordered. Works only for JCR resources - if we cannot determine this, we return null.
      • hasOrderableChildren

        protected Boolean hasOrderableChildren​(com.composum.sling.core.ResourceHandle aResource)
        Returns true if the nodes children are ordered. Works only for JCR resources - if we cannot determine this, we return null.
      • writeParents

        protected void writeParents​(@NotNull
                                    @NotNull ZipOutputStream zipStream,
                                    @NotNull
                                    @NotNull String root,
                                    @Nullable
                                    @Nullable org.apache.sling.api.resource.Resource parent)
                             throws IOException,
                                    javax.jcr.RepositoryException
        Writes all the .content.xml of the parents of root into the zip.
        Throws:
        IOException
        javax.jcr.RepositoryException
      • writeArchive

        public void writeArchive​(@NotNull
                                 @NotNull OutputStream output)
                          throws IOException,
                                 javax.jcr.RepositoryException
        Writes a "naked" Zip about the node: no package metadata, no parent nodes.
        Throws:
        IOException
        javax.jcr.RepositoryException
      • writeIntoZip

        protected void writeIntoZip​(@NotNull
                                    @NotNull ZipOutputStream zipStream,
                                    @NotNull
                                    @NotNull String root,
                                    @NotNull
                                    @NotNull SourceModel.DepthMode depthMode)
                             throws IOException,
                                    javax.jcr.RepositoryException
        Writes a "naked" Zip about the node: no package metadata, no parent nodes. This might include entries about subnodes if {writeDeep}=true, and might include entries about binary properties.
        Parameters:
        zipStream - the stream to write to, not closed.
        depthMode - determines to what extent we write subnodes
        Throws:
        IOException
        javax.jcr.RepositoryException
      • writeFile

        protected void writeFile​(@NotNull
                                 @NotNull ZipOutputStream zipStream,
                                 @NotNull
                                 @NotNull String root,
                                 @NotNull
                                 @NotNull com.composum.sling.core.ResourceHandle file)
                          throws IOException,
                                 javax.jcr.RepositoryException
        Writes the current node as a file node (not the jcr:content but the parent) incl. it's binary data and possibly additional data about nonstandard properties.
        Throws:
        IOException
        javax.jcr.RepositoryException
      • writeBinaryProperties

        protected void writeBinaryProperties​(@NotNull
                                             @NotNull ZipOutputStream zipStream,
                                             @NotNull
                                             @NotNull String root,
                                             @Nullable
                                             @Nullable Queue<String> binaryProperties)
                                      throws IOException
        Writes the binary properties collected in {binaryProperties} into entries in the zip file.
        Throws:
        IOException
      • getZipName

        protected String getZipName​(@NotNull
                                    @NotNull String root,
                                    @NotNull
                                    @NotNull String resourcePath)
        Turns a resource path into a proper name for a zip file with the appropriate encoding of troublesome chars.
      • getZipName

        protected String getZipName​(@NotNull
                                    @NotNull String root)
        Returns the name for the zip entry for this resource.
      • filesystemName

        protected String filesystemName​(String aName)
        Transforms the name into something usable for the filesystem.
      • writeXmlFile

        public void writeXmlFile​(@NotNull
                                 @NotNull Writer writer,
                                 boolean writeDeep)
                          throws IOException,
                                 javax.jcr.RepositoryException
        Writes an XML file for the node, normally .content.xml, including an jcr:content node if present.
        Parameters:
        writeDeep - also write subnodes; if false only properties of the node itself are written but no children (and no jcr:content).
        Throws:
        IOException
        javax.jcr.RepositoryException
      • writeXmlFile

        protected void writeXmlFile​(@NotNull
                                    @NotNull Writer writer,
                                    @NotNull
                                    @NotNull SourceModel.DepthMode depthMode,
                                    @Nullable
                                    @Nullable Queue<String> binaryProperties,
                                    @Nullable
                                    @Nullable Queue<SourceModel> additionalFiles)
                             throws IOException,
                                    javax.jcr.RepositoryException
        Writes an XML file for the node, normally .content.xml, including an jcr:content node if present.
        Parameters:
        depthMode - determines to what extent we write subnodes
        binaryProperties - if given, collects the full paths to binary properties (except jcr:data which is written as a binary file
        additionalFiles - if given, collects the SourceModels that have to be written into another file
        Throws:
        IOException
        javax.jcr.RepositoryException
      • writeSubnodesAsXml

        protected void writeSubnodesAsXml​(@NotNull
                                          @NotNull Writer writer,
                                          @NotNull
                                          @NotNull String indent,
                                          boolean inFullCoverageNode,
                                          @Nullable
                                          @Nullable Queue<String> binaryProperties,
                                          @Nullable
                                          @Nullable Queue<SourceModel> additionalFiles)
                                   throws IOException
        Throws:
        IOException
      • writeXmlSubnode

        protected void writeXmlSubnode​(@NotNull
                                       @NotNull Writer writer,
                                       @NotNull
                                       @NotNull String indent,
                                       boolean inFullCoverageNode,
                                       @Nullable
                                       @Nullable Queue<String> binaryProperties,
                                       @Nullable
                                       @Nullable Queue<SourceModel> additionalFiles)
                                throws IOException
        Writes the node including subnodes as XML, using the base indentation.
        Throws:
        IOException
      • writeSubnodeOrder

        protected void writeSubnodeOrder​(Writer writer,
                                         String indent,
                                         boolean skipContentNode)
                                  throws IOException
        If the node has orderable children, we write a stub node to specify the node order.
        Throws:
        IOException
      • writeNamespaceAttributes

        protected void writeNamespaceAttributes​(Writer writer,
                                                List<String> namespaces)
                                         throws javax.jcr.RepositoryException,
                                                IOException
        Throws:
        javax.jcr.RepositoryException
        IOException
      • escapeXmlName

        public String escapeXmlName​(String propertyName)
      • getRenderingType

        protected SourceModel.RenderingType getRenderingType​(org.apache.sling.api.resource.Resource aResource,
                                                             boolean inFullCoverageNode)
        This encodes what nodes are presented in which way nodes are represented in a Zip / Package.
        Parameters:
        inFullCoverageNode - if true we suppress folders - e.g. if we are within a jcr:content node or an RenderingType.XMLFILE node.
      • isFullCoverageNode

        protected boolean isFullCoverageNode()