This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch issue/SLING-11865-with-revert in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-cpconverter.git
commit 3c0cb4101580e61971958e5a57a6f1ce9cf34889 Author: Robert Munteanu <[email protected]> AuthorDate: Mon May 8 16:22:05 2023 +0200 Revert "SLING-11421 Use FileVault serialization classes for generating enhanced (#161)" This reverts commit 084e751fb5c170eeee9b701ceee9cee1cfb27aac. --- pom.xml | 15 +- ...BundleSlingInitialContentJarEntryExtractor.java | 12 +- .../ContentPackageEntryPathComputer.java | 74 ++++++ .../slinginitialcontent/DocViewTreeNode.java | 87 ------ .../SlingInitialContentBundleEntryMetaData.java | 26 +- ...InitialContentBundleEntryMetaDataCollector.java | 6 +- .../VaultContentXMLContentCreator.java | 293 ++++++++------------- .../XMLNodeToXMLFileWriter.java | 111 ++++++++ .../slinginitialcontent/xmlbuffer/XMLNode.java | 158 +++++++++++ .../createpath/CreatePathSegmentProcessor.java | 2 +- .../BundleEntryHandleSlingInitialContentTest.java | 86 +++--- ...aem632-project.core-0.0.1-SNAPSHOT-escaping.jar | Bin 16178 -> 0 bytes .../handlers/bundle-entry-xmls/11mumbojumbo.xml | 5 +- .../handlers/bundle-entry-xmls/homepage.xml | 4 +- .../bundle-entry-xmls/include-redirectStatus.xml | 26 +- .../handlers/bundle-entry-xmls/nodeName.xml | 5 +- .../cpconverter/handlers/bundle-entry-xmls/xyz.xml | 5 +- .../handlers/escaping-test/.content.xml | 4 - .../en.json.dir/.content.xml | 5 +- 19 files changed, 538 insertions(+), 386 deletions(-) diff --git a/pom.xml b/pom.xml index bbd539e..3e5ff31 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ <sling.java.version>8</sling.java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <picocli.version>3.6.0</picocli.version> - <org.apache.jackrabbit.vault.version>3.6.8</org.apache.jackrabbit.vault.version> + <org.apache.jackrabbit.vault.version>3.6.0</org.apache.jackrabbit.vault.version> <jackrabbit-api.version>1.42.0</jackrabbit-api.version> <jackrabbit-spi-commons.version>2.20.4</jackrabbit-spi-commons.version> <license-maven-plugin.version>1.16</license-maven-plugin.version> @@ -115,6 +115,12 @@ <scope>compile</scope> </dependency> + <dependency> + <groupId>stax-utils</groupId> + <artifactId>stax-utils</artifactId> + <version>snapshot-20040917</version> + <scope>compile</scope> + </dependency> <dependency> <groupId>org.apache.jackrabbit</groupId> <artifactId>jackrabbit-spi-commons</artifactId> @@ -132,12 +138,6 @@ <version>2.11.0</version> <scope>compile</scope> </dependency> - <dependency> - <groupId>org.apache.commons</groupId> - <artifactId>commons-lang3</artifactId> - <version>3.6</version> - <scope>compile</scope> - </dependency> <!-- | Sling Feature Model libraries @@ -379,7 +379,6 @@ <exclude>src/main/resources/META-INF/services/org.apache.sling.feature.cpconverter.handlers.EntryHandler</exclude> <exclude>src/main/legal/NOTICE-with-deps</exclude> <exclude>src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/**</exclude> - <exclude>src/test/resources/org/apache/sling/feature/cpconverter/handlers/escaping-test/**</exclude> <exclude>src/test/resources/org/apache/sling/feature/cpconverter/handlers/i18n-jsonfile-xml-descriptor-test/**</exclude> </excludes> </configuration> diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/BundleSlingInitialContentJarEntryExtractor.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/BundleSlingInitialContentJarEntryExtractor.java index ccd0e09..2684d5d 100644 --- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/BundleSlingInitialContentJarEntryExtractor.java +++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/BundleSlingInitialContentJarEntryExtractor.java @@ -73,7 +73,7 @@ class BundleSlingInitialContentJarEntryExtractor { @NotNull Set<SlingInitialContentBundleEntryMetaData> collectedSlingInitialContentBundleEntries) throws IOException, ConverterException { String repositoryPath = slingInitialContentBundleEntryMetaData.getRepositoryPath(); - File file = slingInitialContentBundleEntryMetaData.getSourceFile(); + File file = slingInitialContentBundleEntryMetaData.getTargetFile(); PathEntry pathEntryValue = slingInitialContentBundleEntryMetaData.getPathEntry(); // all entry paths used by entry handlers start with "/" String contentPackageEntryPath = SLASH + org.apache.jackrabbit.vault.util.Constants.ROOT_DIR + PlatformNameFormat.getPlatformPath(repositoryPath); @@ -92,12 +92,16 @@ class BundleSlingInitialContentJarEntryExtractor { repositoryPath = FilenameUtils.removeExtension(repositoryPath); boolean isFileDescriptorEntry = isFileDescriptor(collectedSlingInitialContentBundleEntries, contentPackageEntryPath); - VaultContentXMLContentCreator contentCreator = new VaultContentXMLContentCreator(repositoryPath, docViewOutput, context.getNamespaceRegistry(), packageAssembler, isFileDescriptorEntry); + VaultContentXMLContentCreator contentCreator = new VaultContentXMLContentCreator(StringUtils.substringBeforeLast(repositoryPath, "/"), docViewOutput, context.getNamespaceRegistry(), packageAssembler, isFileDescriptorEntry); + + + if (file.getName().endsWith(".xml")) { + contentCreator.setIsXmlProcessed(); + } contentReader.parse(file.toURI().toURL(), contentCreator); - // finish is not always called through the ContentReader unfortunately + contentPackageEntryPath = new ContentPackageEntryPathComputer(collectedSlingInitialContentBundleEntries, contentPackageEntryPath, contentCreator).compute(); contentCreator.finish(); - contentPackageEntryPath = contentCreator.getContentPackageEntryPath(); } catch (IOException e) { throw new IOException("Can not parse " + file, e); diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/ContentPackageEntryPathComputer.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/ContentPackageEntryPathComputer.java new file mode 100644 index 0000000..96ab646 --- /dev/null +++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/ContentPackageEntryPathComputer.java @@ -0,0 +1,74 @@ +/* + * 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.feature.cpconverter.handlers.slinginitialcontent; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +import static org.apache.jackrabbit.vault.util.Constants.DOT_CONTENT_XML; +import static org.apache.sling.feature.cpconverter.shared.ConverterConstants.SLASH; + +/** + * Performs re-computation of the ContentPackagePath of the bundle entry (Sling Initial Content) + */ +class ContentPackageEntryPathComputer { + + private final Set<SlingInitialContentBundleEntryMetaData> bundleEntries; + private final String contentPackageEntryPath; + private final VaultContentXMLContentCreator contentCreator; + + ContentPackageEntryPathComputer(@NotNull Set<SlingInitialContentBundleEntryMetaData> bundleEntries, + @NotNull final String contentPackageEntryPath, + @NotNull VaultContentXMLContentCreator contentCreator) { + this.bundleEntries = bundleEntries; + this.contentPackageEntryPath = contentPackageEntryPath; + this.contentCreator = contentCreator; + } + + @NotNull + String compute() { + + String recomputedContentPackageEntryPath = FilenameUtils.removeExtension(contentPackageEntryPath); + + // this covers the case of having a primary node name defined in the xml/json descriptor itself. + // if this is set, we need to use it in the path. + if (StringUtils.isNotBlank(contentCreator.getPrimaryNodeName())) { + //custom node name + recomputedContentPackageEntryPath = StringUtils.substringBeforeLast(recomputedContentPackageEntryPath, SLASH); + recomputedContentPackageEntryPath = recomputedContentPackageEntryPath + SLASH + contentCreator.getPrimaryNodeName(); + } + + final String checkIfRecomputedPathCandidate = StringUtils.removeStart(recomputedContentPackageEntryPath, "/jcr_root"); + // check if the resulting candidate matches one of the repositoryPaths in the bundle entries we have. + // for example /apps/testJsonFile.json.xml (descriptor entry) + // will match /apps/testJsonFile.json (file entry) + if (bundleEntries.stream().anyMatch(bundleEntry -> StringUtils.equals(checkIfRecomputedPathCandidate, bundleEntry.getRepositoryPath()))) { + //we are dealing with a file descriptor here + recomputedContentPackageEntryPath = recomputedContentPackageEntryPath + ".dir/" + DOT_CONTENT_XML; + } else { + // in this case we are dealing with a folder descriptor. for example: + // /apps/testJsonFolder.json + // we want it to end up in the following format: /apps/testJsonFolder/.content.xml in our assembler. + recomputedContentPackageEntryPath = recomputedContentPackageEntryPath + SLASH + DOT_CONTENT_XML; + } + + return recomputedContentPackageEntryPath; + } +} diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/DocViewTreeNode.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/DocViewTreeNode.java deleted file mode 100644 index 1676683..0000000 --- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/DocViewTreeNode.java +++ /dev/null @@ -1,87 +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.feature.cpconverter.handlers.slinginitialcontent; - -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -import javax.jcr.NamespaceException; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; - -import org.apache.jackrabbit.spi.Name; -import org.apache.jackrabbit.spi.commons.conversion.NameResolver; -import org.apache.jackrabbit.spi.commons.name.NameConstants; -import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver; -import org.apache.jackrabbit.vault.util.DocViewNode2; -import org.apache.jackrabbit.vault.util.DocViewProperty2; - -/** - * Represents a serializable docview node inside a tree hierarchy. - * - */ -public class DocViewTreeNode { - - private final List<DocViewTreeNode> children; - private final Name name; - private final String parentPath; - private final Collection<DocViewProperty2> properties; - - public DocViewTreeNode(String parentPath, Name name, Collection<DocViewProperty2> properties) { - children = new LinkedList<>(); - this.name = name; - this.properties = properties; - this.parentPath = parentPath; - } - - public void addChild(DocViewTreeNode child) { - children.add(child); - } - - void write(XMLStreamWriter writer, NamespaceResolver nsResolver) throws NamespaceException, XMLStreamException { - write(writer, nsResolver, Collections.emptyList()); - } - - public void write(XMLStreamWriter writer, NamespaceResolver nsResolver, Iterable<String> nsPrefixes) throws NamespaceException, XMLStreamException { - DocViewNode2 docViewNode = new DocViewNode2(name, properties); - docViewNode.writeStart(writer, nsResolver, nsPrefixes); - for (DocViewTreeNode child : children) { - child.write(writer, nsResolver); - } - DocViewNode2.writeEnd(writer); - } - - public Collection<DocViewProperty2> getProperties() { - return properties; - } - - /** - * - * @param nameResolver - * @return the absolute JCR path (in qualified form) of the node represented by this class - * @throws NamespaceException - */ - public String getPath(NameResolver nameResolver) throws NamespaceException { - if (NameConstants.JCR_ROOT.equals(name)) { - return parentPath; - } else { - return parentPath + "/" + nameResolver.getJCRName(name); - } - } -} diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/SlingInitialContentBundleEntryMetaData.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/SlingInitialContentBundleEntryMetaData.java index 33a345e..d8b15be 100644 --- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/SlingInitialContentBundleEntryMetaData.java +++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/SlingInitialContentBundleEntryMetaData.java @@ -23,45 +23,33 @@ import java.io.File; import java.util.Objects; /** - * Holds Initial Content meta data for a single file + * Holds BundleEntry MetaData. */ class SlingInitialContentBundleEntryMetaData { - private final File sourceFile; + private final File targetFile; private final PathEntry pathEntry; private final String repositoryPath; SlingInitialContentBundleEntryMetaData( - @NotNull File sourceFile, + @NotNull File targetFile, @NotNull PathEntry pathEntry, @NotNull String repositoryPath) { - this.sourceFile = sourceFile; + this.targetFile = targetFile; this.pathEntry = pathEntry; this.repositoryPath = repositoryPath; } - /** - * - * @return the initial content file which is supposed to be installed to {@link #getRepositoryPath()} - */ @NotNull - File getSourceFile() { - return sourceFile; + File getTargetFile() { + return targetFile; } - /** - * - * @return the path entry covering the file {@link #sourceFile} (maybe for a parent) - */ @NotNull PathEntry getPathEntry() { return pathEntry; } - /** - * - * @return the absolute target repository root path of the initial content inside {@link #sourceFile} - */ @NotNull String getRepositoryPath() { return repositoryPath; @@ -77,7 +65,7 @@ class SlingInitialContentBundleEntryMetaData { @Override public int hashCode() { - return Objects.hash(sourceFile, pathEntry, repositoryPath); + return Objects.hash(targetFile, pathEntry, repositoryPath); } @Override diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/SlingInitialContentBundleEntryMetaDataCollector.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/SlingInitialContentBundleEntryMetaDataCollector.java index e8f85fd..152e46e 100644 --- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/SlingInitialContentBundleEntryMetaDataCollector.java +++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/SlingInitialContentBundleEntryMetaDataCollector.java @@ -181,15 +181,15 @@ class SlingInitialContentBundleEntryMetaDataCollector { @NotNull private SlingInitialContentBundleEntryMetaData createSlingInitialContentBundleEntry(@NotNull BundleSlingInitialContentExtractContext context, - @NotNull File sourceFile) throws UnsupportedEncodingException { - final String entryName = StringUtils.replace(StringUtils.substringAfter(sourceFile.getPath(), basePath + File.separator), File.separator, ZIP_ENTRY_SEPARATOR); + @NotNull File targetFile) throws UnsupportedEncodingException { + final String entryName = StringUtils.replace(StringUtils.substringAfter(targetFile.getPath(), basePath + File.separator), File.separator, ZIP_ENTRY_SEPARATOR); final PathEntry pathEntryValue = context.getPathEntryList().stream().filter( pathEntry -> checkIfPathStartsWithOrIsEqual(pathEntry.getPath(), entryName, ZIP_ENTRY_SEPARATOR) ).findFirst().orElseThrow(NullPointerException::new); final String target = pathEntryValue.getTarget(); // https://sling.apache.org/documentation/bundles/content-loading-jcr-contentloader.html#file-name-escaping String repositoryPath = (target != null ? target : "/") + URLDecoder.decode(entryName.substring(pathEntryValue.getPath().length()), "UTF-8"); - return new SlingInitialContentBundleEntryMetaData(sourceFile, pathEntryValue, repositoryPath); + return new SlingInitialContentBundleEntryMetaData(targetFile, pathEntryValue, repositoryPath); } diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/VaultContentXMLContentCreator.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/VaultContentXMLContentCreator.java index b94eea7..8c92dfd 100644 --- a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/VaultContentXMLContentCreator.java +++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/VaultContentXMLContentCreator.java @@ -16,272 +16,189 @@ */ package org.apache.sling.feature.cpconverter.handlers.slinginitialcontent; -import static org.apache.sling.feature.cpconverter.shared.ConverterConstants.SLASH; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.math.BigDecimal; -import java.time.ZoneOffset; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.TimeZone; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.jcr.NamespaceException; -import javax.jcr.NamespaceRegistry; -import javax.jcr.PropertyType; -import javax.jcr.RepositoryException; -import javax.jcr.Value; -import javax.jcr.ValueFactory; -import javax.xml.stream.FactoryConfigurationError; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; - import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.jackrabbit.JcrConstants; -import org.apache.jackrabbit.spi.Name; -import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver; -import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver; -import org.apache.jackrabbit.spi.commons.name.NameConstants; -import org.apache.jackrabbit.util.Text; -import org.apache.jackrabbit.value.ValueFactoryImpl; -import org.apache.jackrabbit.vault.fs.io.DocViewFormat; -import org.apache.jackrabbit.vault.util.Constants; -import org.apache.jackrabbit.vault.util.DocViewProperty2; -import org.apache.jackrabbit.vault.util.PlatformNameFormat; -import org.apache.jackrabbit.vault.util.xml.serialize.FormattingXmlStreamWriter; +import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.xmlbuffer.XMLNode; import org.apache.sling.feature.cpconverter.vltpkg.JcrNamespaceRegistry; import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler; import org.apache.sling.jcr.contentloader.ContentCreator; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.jcr.RepositoryException; +import javax.jcr.Value; +import javax.xml.stream.XMLStreamException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; /** - * ContentCreator implementation to write FileVault enhanced DocView XML files (to be packaged into a VaultPackage) + * ContentCreator substitute to create valid XML files to be packaged into a VaultPackage to be installed later */ public class VaultContentXMLContentCreator implements ContentCreator { private static final String ACL_NOT_SUPPORTED_MSG = "Sling Initial Content - ACL statements are not supported yet . SLING issue: https://issues.apache.org/jira/browse/SLING-11060"; + private final String repositoryPath; + private final OutputStream targetOutputStream; private final VaultPackageAssembler packageAssembler; - private final Queue<DocViewTreeNode> currentNodeStack = Collections.asLifoQueue(new ArrayDeque<>()); + private final LinkedList<XMLNode> parentNodePathStack = new LinkedList<>(); private final JcrNamespaceRegistry namespaceRegistry; - private final NamePathResolver npResolver; - private final XMLStreamWriter writer; - private String rootPath; + private final boolean isFileDescriptorEntry; - private boolean isFinished = false; - private DocViewTreeNode rootNode = null; + private boolean isFirstElement = true; + private boolean finished = false; + private boolean xmlProcessed = false; + private String primaryNodeName; + private XMLNode currentNode; VaultContentXMLContentCreator(@NotNull String repositoryPath, @NotNull OutputStream targetOutputStream, @NotNull JcrNamespaceRegistry namespaceRegistry, @NotNull VaultPackageAssembler packageAssembler, - boolean isFileDescriptorEntry) throws RepositoryException, FactoryConfigurationError { + boolean isFileDescriptorEntry) { + this.repositoryPath = repositoryPath; + this.targetOutputStream = targetOutputStream; this.packageAssembler = packageAssembler; this.namespaceRegistry = namespaceRegistry; this.isFileDescriptorEntry = isFileDescriptorEntry; - this.npResolver = new DefaultNamePathResolver((NamespaceRegistry)namespaceRegistry); - try { - writer = FormattingXmlStreamWriter.create(targetOutputStream, new DocViewFormat().getXmlOutputFormat()); - } catch (XMLStreamException e) { - throw new RepositoryException("Cannot create XML Writer " + e, e); - } - rootPath = repositoryPath; } - /** - * The absolute entry path inside the content package ZIP for the generated docview.xml - * @throws NamespaceException - */ - public String getContentPackageEntryPath() throws NamespaceException { - String suffix; - if (isFileDescriptorEntry) { - // it is potentially https://jackrabbit.apache.org/filevault/vaultfs.html#extended-file-aggregates (and may have file name clashes otherwise with the binary file in the content package) - suffix = ".dir" + "/" + Constants.DOT_CONTENT_XML; - } else { - suffix = "/" + Constants.DOT_CONTENT_XML; - } - return SLASH + org.apache.jackrabbit.vault.util.Constants.ROOT_DIR + PlatformNameFormat.getPlatformPath(rootNode.getPath(npResolver)) + suffix; + void setIsXmlProcessed() { + this.xmlProcessed = true; } @Override public void createNode(String name, String primaryNodeType, String[] mixinNodeTypes) throws RepositoryException { - final Name currentNodeName; - if (rootNode == null) { - currentNodeName = NameConstants.JCR_ROOT; - if (StringUtils.isNotBlank(name)) { - // adjust root path in case the first node has an explicit name - rootPath = Text.getRelativeParent(rootPath, 1) + "/" + name; - } + + final String elementName; + final String jcrNodeName; + if (xmlProcessed && isFirstElement) { + elementName = "jcr:root"; + primaryNodeName = name; + jcrNodeName = name; + isFirstElement = false; + } else if (StringUtils.isNotBlank(name)) { + elementName = getValidElementName(name); + jcrNodeName = name; } else { - currentNodeName = npResolver.getQName(name); + elementName = "jcr:root"; + jcrNodeName = null; } - // if we are dealing with a descriptor file, we should use nt:file as default primaryType. + + final String basePath; + if (parentNodePathStack.isEmpty()) { + basePath = repositoryPath; + } else { + StringBuilder basePathBuilder = new StringBuilder(repositoryPath); + for (Iterator<XMLNode> xmlNodeIterator = parentNodePathStack.descendingIterator(); xmlNodeIterator.hasNext(); ) { + XMLNode parent = xmlNodeIterator.next(); + String parentJcrNodeName = parent.getJcrNodeName(); + if (StringUtils.isNotBlank(parentJcrNodeName)) { + basePathBuilder.append("/"); + basePathBuilder.append(parentJcrNodeName); + } + } + basePath = basePathBuilder.toString(); + } + + //if we are dealing with a descriptor file, we should use nt:file as default primaryType. + String defaultNtType = isFileDescriptorEntry ? JcrConstants.NT_FILE : JcrConstants.NT_UNSTRUCTURED; String toUsePrimaryNodeType = StringUtils.isNotBlank(primaryNodeType) ? primaryNodeType : defaultNtType; - List<DocViewProperty2> currentProperties = new ArrayList<>(); - currentProperties.add(new DocViewProperty2(NameConstants.JCR_PRIMARYTYPE, toUsePrimaryNodeType)); + XMLNode intermediateNode = new XMLNode(packageAssembler, basePath, elementName, jcrNodeName, toUsePrimaryNodeType, mixinNodeTypes); + //add the created node to the correct parent if present + if (currentNode != null) { + currentNode.addChildNode(elementName, intermediateNode); + } + //switch the current node + currentNode = intermediateNode; + if (ArrayUtils.isNotEmpty(mixinNodeTypes)) { - currentProperties.add(new DocViewProperty2(NameConstants.JCR_MIXINTYPES, Arrays.asList(mixinNodeTypes))); + currentNode.addProperty(JcrConstants.JCR_MIXINTYPES, "[" + String.join(",", mixinNodeTypes) + "]"); } - final DocViewTreeNode newNode; - if (rootNode == null) { - newNode = new DocViewTreeNode(rootPath, currentNodeName, currentProperties); - rootNode = newNode; - } else { - newNode = new DocViewTreeNode(rootNode.getPath(npResolver), currentNodeName, currentProperties); - currentNodeStack.element().addChild(newNode); + + parentNodePathStack.push(currentNode); + } + + @NotNull + private static String getValidElementName(@NotNull String name) { + if (StringUtils.isNumeric(name.substring(0, 1))) { + return "_" + name; } - currentNodeStack.add(newNode); + return name; + } + + @Nullable + public String getPrimaryNodeName() { + return primaryNodeName; } @Override - public void finishNode() throws RepositoryException { - this.currentNodeStack.remove(); + public void finishNode() { + if (parentNodePathStack.size() > 1) { + this.parentNodePathStack.pop(); + } + this.currentNode = this.parentNodePathStack.peek(); } @Override public void finish() throws RepositoryException { - if (isFinished) { + + if (finished) { return; } - isFinished = true; try { - rootNode.write(writer, namespaceRegistry, Arrays.asList(namespaceRegistry.getPrefixes())); - writer.close(); + XMLNodeToXMLFileWriter writer = new XMLNodeToXMLFileWriter(currentNode, targetOutputStream, namespaceRegistry); + writer.write(); + finished = true; } catch (XMLStreamException e) { - throw new RepositoryException("Cannot close XML writer " + e, e); + throw new RepositoryException(e); } } + @Override public void createProperty(String name, int propertyType, String value) throws RepositoryException { - // add explicit type due to https://issues.apache.org/jira/browse/JCRVLT-693 - currentNodeStack.peek().getProperties().add(new DocViewProperty2(npResolver.getQName(name), value, propertyType == PropertyType.UNDEFINED ? PropertyType.STRING : propertyType)); + currentNode.addProperty(name, propertyType, value); } + @Override public void createProperty(String name, int propertyType, String[] values) throws RepositoryException { - // add explicit type due to https://issues.apache.org/jira/browse/JCRVLT-693 - currentNodeStack.peek().getProperties().add(new DocViewProperty2(npResolver.getQName(name), Arrays.asList(values), propertyType == PropertyType.UNDEFINED ? PropertyType.STRING : propertyType)); + currentNode.addProperty(name, propertyType, values); } + @Override public void createProperty(String name, Object value) throws RepositoryException { - // store binaries outside of docview xml - Value jcrValue = createValue(name, value, -1); - DocViewProperty2 property = DocViewProperty2.fromValues(npResolver.getQName(name), new Value[] { jcrValue }, jcrValue.getType(), false, false, false); - currentNodeStack.peek().getProperties().add(property); + currentNode.addProperty(name, value); } + @Override public void createProperty(String name, Object[] values) throws RepositoryException { - try { - AtomicInteger index = new AtomicInteger(); - Value[] jcrValues = Arrays.stream(values).map(v -> { - try { - return createValue(name,v, index.getAndIncrement()); - } catch (RepositoryException e) { - throw new UncheckedRepositoryException(e); - } - }).toArray(Value[]::new); - final int type; - if (jcrValues.length == 0) { - type = PropertyType.STRING; - } else { - type = jcrValues[0].getType(); - } - DocViewProperty2 property = DocViewProperty2.fromValues(npResolver.getQName(name), jcrValues, type, true, false, false); - currentNodeStack.peek().getProperties().add(property); - } catch (UncheckedRepositoryException e) { - throw e.getCause(); - } - } - - static final class UncheckedRepositoryException extends RuntimeException { - - private static final long serialVersionUID = 1L; - - public UncheckedRepositoryException(RepositoryException e) { - super(e); - } - - @Override - public synchronized RepositoryException getCause() { - return (RepositoryException) super.getCause(); - } - } - - private Value createValue(String name, Object value, int index) throws RepositoryException { - ValueFactory valueFactory = ValueFactoryImpl.getInstance(); - final Value jcrValue; - if (value instanceof String) { - jcrValue = valueFactory.createValue((String)value); - } else if (value instanceof Long) { - jcrValue = valueFactory.createValue((long)value); - } else if (value instanceof Double) { - jcrValue = valueFactory.createValue((Double)value); - } else if (value instanceof BigDecimal) { - jcrValue = valueFactory.createValue((BigDecimal)value); - } else if (value instanceof Date) { - Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.UTC), Locale.ROOT); - calendar.setTime((Date)value); - jcrValue = valueFactory.createValue(calendar); - } else if (value instanceof Calendar) { - jcrValue = valueFactory.createValue((Calendar)value); - } else if (value instanceof Boolean) { - jcrValue = valueFactory.createValue((Boolean)value); - } else if (value instanceof InputStream) { - // binaries are always stored outside the docview xml (https://jackrabbit.apache.org/filevault/vaultfs.html#Binary_Properties) - String binaryPropertyEntryName = PlatformNameFormat.getPlatformName(name) + ((index != -1) ? "[" + index + "]" : "") + ".binary"; - createBinary((InputStream)value, SLASH + binaryPropertyEntryName); - jcrValue = valueFactory.createValue("", PropertyType.BINARY); - } else { - throw new UnsupportedOperationException("Unsupported value type " + value.getClass()); - } - return jcrValue; - } - - private void createBinary(InputStream value, String suffix) throws RepositoryException { - // this is called inside the node of the child - String path = org.apache.jackrabbit.vault.util.Constants.ROOT_DIR + PlatformNameFormat.getPlatformPath(currentNodeStack.peek().getPath(npResolver)) + suffix; - try { - // write binary directly (not during finish) - packageAssembler.addEntry(path, value); - } catch (IOException e) { - throw new RepositoryException(e); - } + currentNode.addProperty(name, values); } @Override public void createFileAndResourceNode(String name, InputStream data, String mimeType, long lastModified) throws RepositoryException { this.createNode(name, JcrConstants.NT_FILE, null); - createBinary(data, ""); // binary must be created with the name of the nt:file root node this.createNode(JcrConstants.JCR_CONTENT, JcrConstants.NT_RESOURCE, null); - final Date date; // ensure sensible last modification date - if (lastModified >= 0) { - date = new Date(lastModified); - } else { - date = new Date(); + if (lastModified <= 0) { + lastModified = System.currentTimeMillis(); } this.createProperty(JcrConstants.JCR_MIMETYPE, mimeType); - this.createProperty(JcrConstants.JCR_LASTMODIFIED, date); - // the data property does not need to be added to the enhanced docview as already derived from the binary file + this.createProperty(JcrConstants.JCR_LASTMODIFIED, lastModified); + this.createProperty(JcrConstants.JCR_DATA, data); } diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/XMLNodeToXMLFileWriter.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/XMLNodeToXMLFileWriter.java new file mode 100644 index 0000000..272f300 --- /dev/null +++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/XMLNodeToXMLFileWriter.java @@ -0,0 +1,111 @@ +/* + * 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.feature.cpconverter.handlers.slinginitialcontent; + + +import javanet.staxutils.IndentingXMLEventWriter; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.feature.cpconverter.handlers.slinginitialcontent.xmlbuffer.XMLNode; +import org.apache.sling.feature.cpconverter.vltpkg.JcrNamespaceRegistry; +import org.jetbrains.annotations.NotNull; + +import javax.jcr.RepositoryException; +import javax.xml.stream.XMLEventFactory; +import javax.xml.stream.XMLEventWriter; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import static org.apache.jackrabbit.vault.util.JcrConstants.JCR_MIXINTYPES; +import static org.apache.jackrabbit.vault.util.JcrConstants.JCR_PRIMARYTYPE; +import static org.apache.jackrabbit.vault.util.JcrConstants.NT_UNSTRUCTURED; + +/** + * Takes the buffered parent XMLNode and writes it to an actual XML file + */ +class XMLNodeToXMLFileWriter { + + private final XMLNode parentNode; + private final IndentingXMLEventWriter eventWriter; + private final JcrNamespaceRegistry namespaceRegistry; + private final XMLEventFactory eventFactory = XMLEventFactory.newInstance(); + + XMLNodeToXMLFileWriter(@NotNull XMLNode parentNode, + @NotNull OutputStream targetOutputStream, + @NotNull JcrNamespaceRegistry namespaceRegistry) throws XMLStreamException { + this.parentNode = parentNode; + XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(targetOutputStream, StandardCharsets.UTF_8.name()); + this.eventWriter = new IndentingXMLEventWriter( + writer + ); + this.eventWriter.setNewLine("\n"); + this.namespaceRegistry = namespaceRegistry; + this.eventWriter.setNamespaceContext(this.namespaceRegistry); + + } + + void write() throws XMLStreamException, RepositoryException { + + eventWriter.add(eventFactory.createStartDocument()); + writeNode(parentNode, true); + eventWriter.add(eventFactory.createEndDocument()); + + } + + void writeNode(@NotNull XMLNode xmlNode, boolean isFirstElement) throws RepositoryException, XMLStreamException { + + eventWriter.add(eventFactory.createStartElement(StringUtils.EMPTY, StringUtils.EMPTY, xmlNode.getXmlElementName())); + + if (isFirstElement) { + for (String prefix : namespaceRegistry.getPrefixes()) { + eventWriter.add(eventFactory.createNamespace(prefix, namespaceRegistry.getURI(prefix))); + } + } + + String primaryNodeType = xmlNode.getPrimaryNodeType(); + String[] mixinNodeTypes = xmlNode.getMixinNodeTypes(); + + + eventWriter.add(eventFactory.createAttribute(JCR_PRIMARYTYPE, StringUtils.isNotBlank(primaryNodeType) ? primaryNodeType : NT_UNSTRUCTURED)); + + if (ArrayUtils.isNotEmpty(mixinNodeTypes)) { + eventWriter.add(eventFactory.createAttribute(JCR_MIXINTYPES, "[" + String.join(",", mixinNodeTypes) + "]")); + } + + for (Map.Entry<String, String> entry : xmlNode.getVltXmlParsedProperties().entrySet()) { + + if (entry.getKey().equals(JCR_PRIMARYTYPE) || entry.getKey().equals(JCR_MIXINTYPES)) { + continue; + } + + eventWriter.add(eventFactory.createAttribute(entry.getKey(), entry.getValue())); + } + + for (XMLNode node : xmlNode.getChildren().values()) { + writeNode(node, false); + } + + eventWriter.add(eventFactory.createEndElement(StringUtils.EMPTY, StringUtils.EMPTY, xmlNode.getXmlElementName())); + + + } + + +} diff --git a/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/xmlbuffer/XMLNode.java b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/xmlbuffer/XMLNode.java new file mode 100644 index 0000000..dcad4b6 --- /dev/null +++ b/src/main/java/org/apache/sling/feature/cpconverter/handlers/slinginitialcontent/xmlbuffer/XMLNode.java @@ -0,0 +1,158 @@ +/* + * 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.feature.cpconverter.handlers.slinginitialcontent.xmlbuffer; + +import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.jcr.PropertyType; +import javax.jcr.RepositoryException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Represents an XML node in the buffer to be written in a sling initial content package later + */ +public class XMLNode { + + private static final String FORMAT_SINGLE_VALUE = "%s"; + private static final String FORMAT_SINGLE_VALUE_TYPED = "{%s}%s"; + private static final String FORMAT_MULTI_VALUE = "[%s]"; + private static final String FORMAT_MULTI_VALUE_TYPED = "{%s}[%s]"; + + private final String basePath; + private final String xmlElementName; + private final String jcrNodeName; + private final String primaryNodeType; + private final String[] mixinNodeTypes; + + private final VaultPackageAssembler packageAssembler; + private final Map<String, String> vltXmlParsedProperties = new HashMap<>(); + private final Map<String, XMLNode> children = new LinkedHashMap<>(); + + public XMLNode(@NotNull VaultPackageAssembler packageAssembler, + @NotNull String basePath, + @NotNull String xmlElementName, + @Nullable String jcrNodeName, + @NotNull String primaryNodeType, + @Nullable String[] mixinNodeTypes) { + this.packageAssembler = packageAssembler; + this.basePath = basePath; + this.xmlElementName = xmlElementName; + this.jcrNodeName = jcrNodeName; + this.primaryNodeType = primaryNodeType; + this.mixinNodeTypes = mixinNodeTypes; + } + + public void addProperty(@NotNull String name, int propertyType, @NotNull String value) { + String propertyTypeName = PropertyType.nameFromValue(propertyType); + + if (propertyType > 0) { + vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE_TYPED, propertyTypeName, value)); + } else { + vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE, value)); + } + + } + + public void addProperty(@NotNull String name, int propertyType, @NotNull String[] values) { + String propertyTypeName = PropertyType.nameFromValue(propertyType); + + if (propertyType > 0) { + vltXmlParsedProperties.put(name, String.format(FORMAT_MULTI_VALUE_TYPED, propertyTypeName, String.join(",", values))); + } else { + vltXmlParsedProperties.put(name, String.format(FORMAT_MULTI_VALUE, String.join(",", values))); + } + + } + + public void addChildNode(@NotNull String name, @NotNull XMLNode xmlNode) { + this.children.put(name, xmlNode); + } + + public void addProperty(@NotNull String name, @Nullable Object value) throws RepositoryException { + if (value == null) { + return; + } + + if (value instanceof Long) { + vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE_TYPED, "Long", value.toString())); + } else if (value instanceof Date) { + vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE_TYPED, "Date", ((Date) value).toGMTString())); + } else if (value instanceof Calendar) { + vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE_TYPED, "Date", ((Calendar) value).getTime().toGMTString())); + } else if (value instanceof Double) { + vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE_TYPED, "Double", value)); + } else if (value instanceof Boolean) { + boolean theBoolValue = Boolean.parseBoolean(value.toString()); + vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE_TYPED, "Boolean", theBoolValue)); + } else if (value instanceof InputStream) { + vltXmlParsedProperties.put(name, "{Binary}"); + String path = "jcr_root/" + this.basePath; + try { + packageAssembler.addEntry(path, (InputStream) value); + } catch (IOException e) { + throw new RepositoryException(e); + } + + } else { + vltXmlParsedProperties.put(name, String.format(FORMAT_SINGLE_VALUE, value)); + } + + } + + @NotNull + public String getPath() { + return this.basePath + "/" + this.jcrNodeName; + } + + @NotNull + public String getXmlElementName() { + return xmlElementName; + } + + @Nullable + public String getJcrNodeName() { + return jcrNodeName; + } + + @NotNull + public String getPrimaryNodeType() { + return primaryNodeType; + } + + @Nullable + public String[] getMixinNodeTypes() { + return mixinNodeTypes; + } + + @NotNull + public Map<String, XMLNode> getChildren() { + return children; + } + + @NotNull + public Map<String, String> getVltXmlParsedProperties() { + return vltXmlParsedProperties; + } +} diff --git a/src/main/java/org/apache/sling/feature/cpconverter/repoinit/createpath/CreatePathSegmentProcessor.java b/src/main/java/org/apache/sling/feature/cpconverter/repoinit/createpath/CreatePathSegmentProcessor.java index 85a9ea5..70699a1 100644 --- a/src/main/java/org/apache/sling/feature/cpconverter/repoinit/createpath/CreatePathSegmentProcessor.java +++ b/src/main/java/org/apache/sling/feature/cpconverter/repoinit/createpath/CreatePathSegmentProcessor.java @@ -98,7 +98,7 @@ public class CreatePathSegmentProcessor { throw new RuntimeException("A fatal error occurred while parsing the '" + currentContent + "' file, see nested exceptions: " - + e, e); + + e); } return false; } diff --git a/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandleSlingInitialContentTest.java b/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandleSlingInitialContentTest.java index 383d7f8..deaa539 100644 --- a/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandleSlingInitialContentTest.java +++ b/src/test/java/org/apache/sling/feature/cpconverter/handlers/BundleEntryHandleSlingInitialContentTest.java @@ -45,6 +45,8 @@ import java.util.Properties; import java.util.Set; import java.util.jar.JarFile; +import javax.xml.transform.Source; + import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; @@ -66,6 +68,7 @@ import org.apache.sling.feature.cpconverter.shared.ConverterConstants; import org.apache.sling.feature.cpconverter.vltpkg.VaultPackageAssembler; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; +import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -76,6 +79,10 @@ import org.mockito.junit.MockitoJUnitRunner; import org.osgi.framework.Constants; import org.osgi.framework.Version; import org.xml.sax.SAXException; +import org.xmlunit.builder.Input; +import org.xmlunit.diff.ComparisonType; +import org.xmlunit.diff.DOMDifferenceEngine; +import org.xmlunit.diff.DifferenceEngine; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -241,6 +248,7 @@ public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntr String actualXML = IOUtils.toString(archive.getInputSource(jsonFileDescriptorEntry).getByteStream(), UTF_8); assertThat(actualXML).and(expectedXML).areSimilar(); + } } @@ -401,6 +409,9 @@ public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntr converter.deployPackages(); + + + // verify generated package try (VaultPackage vaultPackage = new PackageManagerImpl().open(new File(targetFolder, "mysite.core-apps-1.0.0-SNAPSHOT-cp2fm-converted.zip")); Archive archive = vaultPackage.getArchive()) { @@ -471,7 +482,7 @@ public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntr String expectedXML = IOUtils.toString(getClass().getResourceAsStream("bundle-entry-xmls/include-redirectStatus.xml"), UTF_8); String actualXML = IOUtils.toString(xmlFile, UTF_8); - assertThat(actualXML).and(expectedXML).areSimilar(); + assertThat(expectedXML).and(actualXML).areSimilar(); } } @@ -651,69 +662,42 @@ public class BundleEntryHandleSlingInitialContentTest extends AbstractBundleEntr } } - @Test - public void testSlingInitialContentEscapingPropertyValues() throws Exception { - setUpArchive("/jcr_root/apps/gav/install/aem-aem632-project.core-0.0.1-SNAPSHOT-escaping.jar", "aem-aem632-project.core-0.0.1-SNAPSHOT-escaping.jar"); - DefaultEntryHandlersManager handlersManager = new DefaultEntryHandlersManager(); - converter.setEntryHandlersManager(handlersManager); - - File targetFolder = tmpFolder.newFolder(); - when(converter.getArtifactsDeployer()).thenReturn(new SimpleFolderArtifactsDeployer(targetFolder)); - when(converter.isSubContentPackageIncluded("/jcr_root/apps/gav/install/aem-aem632-project.core-0.0.1-SNAPSHOT-escaping.jar-APPLICATION")).thenReturn(true); - - VaultPackageAssembler assembler = Mockito.mock(VaultPackageAssembler.class); - Properties props = new Properties(); - props.setProperty(PackageProperties.NAME_GROUP, "com.aem632"); - props.setProperty(PackageProperties.NAME_NAME, "aem-aem632-project.core"); - props.setProperty(PackageProperties.NAME_VERSION, "0.0.1-SNAPSHOT"); - when(assembler.getPackageProperties()).thenReturn(props); - converter.setMainPackageAssembler(assembler); - converter.setAclManager(new DefaultAclManager()); - - BundleSlingInitialContentExtractor extractor = new BundleSlingInitialContentExtractor(); - - handler.setBundleSlingInitialContentExtractor(extractor); - handler.setSlingInitialContentPolicy(SlingInitialContentPolicy.EXTRACT_AND_REMOVE); - handler.handle("/jcr_root/apps/gav/install/aem-aem632-project.core-0.0.1-SNAPSHOT-escaping.jar", archive, entry, converter); - - extractor.addRepoInitExtension(converter.getAssemblers(), featuresManager); - - converter.deployPackages(); - // verify generated package - try (VaultPackage vaultPackage = new PackageManagerImpl().open(new File(targetFolder, "aem-aem632-project.core-apps-0.0.1-SNAPSHOT-cp2fm-converted.zip")); - Archive archive = vaultPackage.getArchive()) { - archive.open(true); - PackageId targetId = PackageId.fromString("com.aem632:aem-aem632-project.core-apps:0.0.1-SNAPSHOT-cp2fm-converted"); - assertEquals(targetId, vaultPackage.getId()); - Entry entry = archive.getEntry("jcr_root/apps/aem632/core/test/.content.xml"); - assertNotNull("Archive does not contain expected item", entry); - - String expectedXML = IOUtils.toString(getClass().getResourceAsStream("escaping-test/.content.xml"), UTF_8); - String actualXML = IOUtils.toString(archive.getInputSource(entry).getByteStream(), UTF_8); - assertThat(actualXML).and(expectedXML).areSimilar(); - } - } - private void assertResultingEntry(Archive archive, String entryKey) throws IOException, SAXException { InputStream xmlFile = archive.getInputSource(archive.getEntry("jcr_root/apps/mysite/components/global/" + entryKey +"/.content.xml")).getByteStream(); InputStream expectedXmlFileStream = getClass().getResourceAsStream("bundle-entry-xmls/" + entryKey + ".xml"); String expectedXML = IOUtils.toString(expectedXmlFileStream, UTF_8); String actualXML = IOUtils.toString(xmlFile, UTF_8); - assertThat(actualXML).and(expectedXML).areSimilar(); + + Source control = Input.fromString(expectedXML).build(); + Source test = Input.fromString(actualXML).build(); + + DifferenceEngine diff = new DOMDifferenceEngine(); + diff.addDifferenceListener((comparison, outcome) -> { + + if(comparison.getType() == ComparisonType.CHILD_NODELIST_LENGTH){ + //this comparison is buggy so we can't use it. + return; + } + + String actualString = comparison.getTestDetails().getValue().toString(); + String expectedString = comparison.getControlDetails().getValue().toString(); + if(!actualString.trim().equals(expectedString.trim())){ + Assert.fail("difference found in XML: " + actualString + " vs " + expectedString); + } + }); + diff.compare(control, test); } private void assertPageStructureFromEntry(Archive archive, String basePath, String pageName, String... files) throws IOException { - String entryPath = basePath + "/" + pageName + "/.content.xml"; - Entry contentXml = archive.getEntry(entryPath); - assertNotNull("could not find entry path " + entryPath + " in archive " + archive, contentXml); + Entry contentXml = archive.getEntry( basePath + "/" + pageName + "/.content.xml"); + assertNotNull(contentXml); Entry pageXml = archive.getEntry( basePath + "/" + pageName + ".xml"); assertNull(pageXml); for(String file: files){ - entryPath = basePath + "/" + pageName + "/" + file; - Entry expectedEntry = archive.getEntry(entryPath); - assertNotNull("could not find entry path " + entryPath + " in archive " + archive, expectedEntry); + Entry expectedEntry = archive.getEntry( basePath + "/" + pageName + "/" + file); + assertNotNull(expectedEntry); } } diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/aem-aem632-project.core-0.0.1-SNAPSHOT-escaping.jar b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/aem-aem632-project.core-0.0.1-SNAPSHOT-escaping.jar deleted file mode 100644 index 37e0f13..0000000 Binary files a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/aem-aem632-project.core-0.0.1-SNAPSHOT-escaping.jar and /dev/null differ diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/11mumbojumbo.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/11mumbojumbo.xml index 1cbee8f..de1f197 100644 --- a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/11mumbojumbo.xml +++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/11mumbojumbo.xml @@ -1,4 +1,5 @@ <?xml version='1.0' encoding='UTF-8'?> -<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="cq:Component" prop=" [...] - <someOtherNode jcr:primaryType="nt:unstructured" prop="property value as string" /> +<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="cq:Component" prop=" [...] + <someOtherNode jcr:primaryType="nt:unstructured" prop="{String}property value as string"> + </someOtherNode> </jcr:root> \ No newline at end of file diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/homepage.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/homepage.xml index ba5b304..7bdf717 100644 --- a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/homepage.xml +++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/homepage.xml @@ -1 +1,3 @@ -<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="cq:Component" sling: [...] \ No newline at end of file +<?xml version='1.0' encoding='UTF-8'?> +<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="cq:Component" sling: [...] +</jcr:root> \ No newline at end of file diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/include-redirectStatus.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/include-redirectStatus.xml index 61e9646..a441d1c 100755 --- a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/include-redirectStatus.xml +++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/include-redirectStatus.xml @@ -1,15 +1,17 @@ <?xml version='1.0' encoding='UTF-8'?> <jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="nt:unstructured" mar [...] - <items jcr:primaryType="nt:unstructured"> - <column jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/container"> - <items jcr:primaryType="nt:unstructured"> - <redirectStatus jcr:primaryType="nt:unstructured" defaultValue="301" fieldLabel="io.wcm.handler.link.components.global.include.linkRefContainer.redirectStatus.redirectStatus.fieldLabel" name="./redirectStatus" sling:resourceType="granite/ui/components/coral/foundation/form/select" fieldDescription="io.wcm.handler.link.components.global.include.linkRefContainer.redirectStatus.redirectStatus.fieldDescription"> - <items jcr:primaryType="nt:unstructured"> - <_x0033_01 jcr:primaryType="nt:unstructured" text="io.wcm.handler.link.components.global.include.linkRefContainer.redirectStatus.redirectStatus.301" value="301"/> - <_x0033_02 jcr:primaryType="nt:unstructured" text="io.wcm.handler.link.components.global.include.linkRefContainer.redirectStatus.redirectStatus.302" value="302"/> - </items> - </redirectStatus> - </items> - </column> - </items> + <items jcr:primaryType="nt:unstructured"> + <column jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/container"> + <items jcr:primaryType="nt:unstructured"> + <redirectStatus jcr:primaryType="nt:unstructured" defaultValue="301" fieldLabel="io.wcm.handler.link.components.global.include.linkRefContainer.redirectStatus.redirectStatus.fieldLabel" name="./redirectStatus" sling:resourceType="granite/ui/components/coral/foundation/form/select" fieldDescription="io.wcm.handler.link.components.global.include.linkRefContainer.redirectStatus.redirectStatus.fieldDescription"> + <items jcr:primaryType="nt:unstructured"> + <_301 jcr:primaryType="nt:unstructured" text="io.wcm.handler.link.components.global.include.linkRefContainer.redirectStatus.redirectStatus.301" value="301"> + </_301> + <_302 jcr:primaryType="nt:unstructured" text="io.wcm.handler.link.components.global.include.linkRefContainer.redirectStatus.redirectStatus.302" value="302"> + </_302> + </items> + </redirectStatus> + </items> + </column> + </items> </jcr:root> \ No newline at end of file diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/nodeName.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/nodeName.xml index 5de3ccc..c11e395 100644 --- a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/nodeName.xml +++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/nodeName.xml @@ -1,6 +1,7 @@ <?xml version='1.0' encoding='UTF-8'?> -<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="nt:unstructured" pro [...] +<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="nt:unstructured" pro [...] <testfile.txt jcr:primaryType="nt:file"> - <jcr:content jcr:lastModified="{Date}1977-06-01T06:00:00.000Z" jcr:mimeType="application/test" jcr:primaryType="nt:resource"/> + <jcr:content jcr:primaryType="nt:resource" jcr:mimeType="application/test" jcr:data="{Binary}" jcr:lastModified="{Long}233992800000"> + </jcr:content> </testfile.txt> </jcr:root> \ No newline at end of file diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/xyz.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/xyz.xml index ac39d93..78dfbe0 100644 --- a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/xyz.xml +++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/bundle-entry-xmls/xyz.xml @@ -1,4 +1,5 @@ <?xml version='1.0' encoding='UTF-8'?> -<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="nt:unstructured" jcr [...] - <someOtherNode jcr:primaryType="nt:unstructured" prop="property value as string"/> +<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" jcr:primaryType="nt:unstructured" jcr [...] + <someOtherNode jcr:primaryType="nt:unstructured" prop="{String}property value as string"> + </someOtherNode> </jcr:root> \ No newline at end of file diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/escaping-test/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/escaping-test/.content.xml deleted file mode 100644 index d58b5f9..0000000 --- a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/escaping-test/.content.xml +++ /dev/null @@ -1,4 +0,0 @@ -<jcr:root xmlns="" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:vlt="http://www.day.com/jcr/vault/1.0" - jcr:primaryType="nt:unstructured" - property1="^/content/wcm-io(-\\w+)?/.+$" - property2="^/content/wcm-io(-\\\\w+)?/.+$"/> \ No newline at end of file diff --git a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/i18n-jsonfile-xml-descriptor-test/en.json.dir/.content.xml b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/i18n-jsonfile-xml-descriptor-test/en.json.dir/.content.xml index 08a0c9d..79aadfd 100755 --- a/src/test/resources/org/apache/sling/feature/cpconverter/handlers/i18n-jsonfile-xml-descriptor-test/en.json.dir/.content.xml +++ b/src/test/resources/org/apache/sling/feature/cpconverter/handlers/i18n-jsonfile-xml-descriptor-test/en.json.dir/.content.xml @@ -2,6 +2,7 @@ <jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:oak="http://jackrabbit.apache.org/oak/ns/1.0" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:my="http://namespace.com/my" - jcr:language="en" + jcr:language="{String}en" jcr:mixinTypes="[mix:language]" - jcr:primaryType="nt:file"/> \ No newline at end of file + jcr:primaryType="nt:file"> +</jcr:root> \ No newline at end of file
