| /* |
| * 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.lang3.ArrayUtils; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.jackrabbit.JcrConstants; |
| 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 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 LinkedList<XMLNode> parentNodePathStack = new LinkedList<>(); |
| private final JcrNamespaceRegistry namespaceRegistry; |
| |
| private final boolean isFileDescriptorEntry; |
| |
| 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) { |
| this.repositoryPath = repositoryPath; |
| this.targetOutputStream = targetOutputStream; |
| this.packageAssembler = packageAssembler; |
| this.namespaceRegistry = namespaceRegistry; |
| this.isFileDescriptorEntry = isFileDescriptorEntry; |
| } |
| |
| void setIsXmlProcessed() { |
| this.xmlProcessed = true; |
| } |
| |
| @Override |
| public void createNode(String name, String primaryNodeType, String[] mixinNodeTypes) throws RepositoryException { |
| |
| 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 { |
| elementName = "jcr:root"; |
| jcrNodeName = null; |
| } |
| |
| |
| 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; |
| 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)) { |
| currentNode.addProperty(JcrConstants.JCR_MIXINTYPES, "[" + String.join(",", mixinNodeTypes) + "]"); |
| } |
| |
| parentNodePathStack.push(currentNode); |
| } |
| |
| @NotNull |
| private static String getValidElementName(@NotNull String name) { |
| if (StringUtils.isNumeric(name.substring(0, 1))) { |
| return "_" + name; |
| } |
| return name; |
| } |
| |
| @Nullable |
| public String getPrimaryNodeName() { |
| return primaryNodeName; |
| } |
| |
| @Override |
| public void finishNode() { |
| if (parentNodePathStack.size() > 1) { |
| this.parentNodePathStack.pop(); |
| } |
| this.currentNode = this.parentNodePathStack.peek(); |
| } |
| |
| @Override |
| public void finish() throws RepositoryException { |
| |
| if (finished) { |
| return; |
| } |
| try { |
| XMLNodeToXMLFileWriter writer = new XMLNodeToXMLFileWriter(currentNode, targetOutputStream, namespaceRegistry); |
| writer.write(); |
| finished = true; |
| } catch (XMLStreamException e) { |
| throw new RepositoryException(e); |
| } |
| } |
| |
| |
| @Override |
| public void createProperty(String name, int propertyType, String value) throws RepositoryException { |
| currentNode.addProperty(name, propertyType, value); |
| } |
| |
| |
| @Override |
| public void createProperty(String name, int propertyType, String[] values) throws RepositoryException { |
| currentNode.addProperty(name, propertyType, values); |
| } |
| |
| |
| @Override |
| public void createProperty(String name, Object value) throws RepositoryException { |
| currentNode.addProperty(name, value); |
| } |
| |
| |
| @Override |
| public void createProperty(String name, Object[] values) throws RepositoryException { |
| 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); |
| this.createNode(JcrConstants.JCR_CONTENT, JcrConstants.NT_RESOURCE, null); |
| |
| // ensure sensible last modification date |
| if (lastModified <= 0) { |
| lastModified = System.currentTimeMillis(); |
| } |
| this.createProperty(JcrConstants.JCR_MIMETYPE, mimeType); |
| this.createProperty(JcrConstants.JCR_LASTMODIFIED, lastModified); |
| this.createProperty(JcrConstants.JCR_DATA, data); |
| } |
| |
| |
| @Override |
| public boolean switchCurrentNode(String subPath, String newNodeType) { |
| throw new UnsupportedOperationException(ACL_NOT_SUPPORTED_MSG); |
| } |
| |
| @Override |
| public void createUser(String name, String password, Map<String, Object> extraProperties) { |
| throw new UnsupportedOperationException(ACL_NOT_SUPPORTED_MSG); |
| } |
| |
| @Override |
| public void createGroup(String name, String[] members, Map<String, Object> extraProperties) { |
| throw new UnsupportedOperationException(ACL_NOT_SUPPORTED_MSG); |
| } |
| |
| @Override |
| public void createAce(String principal, String[] grantedPrivileges, String[] deniedPrivileges, String order) { |
| throw new UnsupportedOperationException(ACL_NOT_SUPPORTED_MSG); |
| } |
| |
| @Override |
| public void createAce(String principalId, String[] grantedPrivilegeNames, String[] deniedPrivilegeNames, |
| String order, Map<String, Value> restrictions, Map<String, Value[]> mvRestrictions, |
| Set<String> removedRestrictionNames) { |
| throw new UnsupportedOperationException(ACL_NOT_SUPPORTED_MSG); |
| } |
| |
| } |