You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2014/06/25 17:06:10 UTC
svn commit: r1605439 - in /sling/trunk/tooling/ide:
api/src/org/apache/sling/ide/serialization/
eclipse-ui/src/org/apache/sling/ide/eclipse/ui/internal/
impl-vlt/src/org/apache/sling/ide/impl/vlt/serialization/
Author: rombert
Date: Wed Jun 25 15:06:09 2014
New Revision: 1605439
URL: http://svn.apache.org/r1605439
Log:
SLING-3697 - Incorrect handling of deeply nested aggregates
- clarified the SerializationData API and contract
- generalize the logic for finding leaf aggregates and constructing
their path to take into account multiple intermediate leaf aggregates
- use the os paths to look for leaf aggregates, instead of repository
paths
Modified:
sling/trunk/tooling/ide/api/src/org/apache/sling/ide/serialization/SerializationData.java
sling/trunk/tooling/ide/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/internal/ImportRepositoryContentAction.java
sling/trunk/tooling/ide/impl-vlt/src/org/apache/sling/ide/impl/vlt/serialization/VltSerializationDataBuilder.java
Modified: sling/trunk/tooling/ide/api/src/org/apache/sling/ide/serialization/SerializationData.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/ide/api/src/org/apache/sling/ide/serialization/SerializationData.java?rev=1605439&r1=1605438&r2=1605439&view=diff
==============================================================================
--- sling/trunk/tooling/ide/api/src/org/apache/sling/ide/serialization/SerializationData.java (original)
+++ sling/trunk/tooling/ide/api/src/org/apache/sling/ide/serialization/SerializationData.java Wed Jun 25 15:06:09 2014
@@ -19,32 +19,42 @@ package org.apache.sling.ide.serializati
public class SerializationData {
private final byte[] contents;
- private final String nameHint;
+ private final String fileName;
private final SerializationKind serializationKind;
- private final String fileOrFolderNameHint;
+ private final String folderPath;
- public static SerializationData empty(String fileOrFolderNameHint, SerializationKind serializationKind) {
- return new SerializationData(fileOrFolderNameHint, null, null, serializationKind);
+ public static SerializationData empty(String folderPath, SerializationKind serializationKind) {
+ return new SerializationData(folderPath, null, null, serializationKind);
}
- public SerializationData(String fileOrFolderNameHint, String nameHint, byte[] contents,
+ public SerializationData(String folderPath, String fileName, byte[] contents,
SerializationKind serializationKind) {
- this.fileOrFolderNameHint = fileOrFolderNameHint;
+ this.folderPath = folderPath;
this.contents = contents;
- this.nameHint = nameHint;
+ this.fileName = fileName;
this.serializationKind = serializationKind;
}
- public String getFileOrFolderNameHint() {
- return fileOrFolderNameHint;
+ /**
+ * @return the path where the serialization data file should be stores, in OS format
+ */
+ public String getFolderPath() {
+ return folderPath;
}
+ /**
+ * @return the contents of the serialization data file
+ */
public byte[] getContents() {
return contents;
}
- public String getNameHint() {
- return nameHint;
+ /**
+ *
+ * @return the name of the serialization data file
+ */
+ public String getFileName() {
+ return fileName;
}
public SerializationKind getSerializationKind() {
@@ -58,7 +68,7 @@ public class SerializationData {
@Override
public String toString() {
- return "[SerializationData# fileOrFolderNameHint: " + fileOrFolderNameHint + ", nameHint: " + nameHint
- + ", serializationKind: " + serializationKind + ", contents?" + (hasContents()) + "]";
+ return "[SerializationData# folderPath: " + folderPath + ", fileName: " + fileName
+ + ", serializationKind: " + serializationKind + ", hasContents: " + (hasContents()) + "]";
}
}
\ No newline at end of file
Modified: sling/trunk/tooling/ide/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/internal/ImportRepositoryContentAction.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/ide/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/internal/ImportRepositoryContentAction.java?rev=1605439&r1=1605438&r2=1605439&view=diff
==============================================================================
--- sling/trunk/tooling/ide/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/internal/ImportRepositoryContentAction.java (original)
+++ sling/trunk/tooling/ide/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/internal/ImportRepositoryContentAction.java Wed Jun 25 15:06:09 2014
@@ -196,31 +196,27 @@ public class ImportRepositoryContentActi
ResourceProxy resource = executeCommand(repository.newListChildrenNodeCommand(path));
- // TODO we should know all node types for which to create files and folders
SerializationData serializationData = builder.buildSerializationData(contentSyncRoot, resource);
logger.trace("For resource at path {0} got serialization data {1}", resource.getPath(), serializationData);
final List<ResourceProxy> resourceChildren = new LinkedList<ResourceProxy>(resource.getChildren());
if (serializationData != null) {
- IPath fileOrFolderPath = contentSyncRootDir.getProjectRelativePath().append(
- serializationData.getFileOrFolderNameHint());
+ IPath serializationFolderPath = contentSyncRootDir.getProjectRelativePath().append(
+ serializationData.getFolderPath());
switch (serializationData.getSerializationKind()) {
case FILE: {
byte[] contents = executeCommand(repository.newGetNodeCommand(path));
- importFile(project, fileOrFolderPath, contents);
+ createFile(project, getPathForPlainFileNode(resource, serializationFolderPath), contents);
if (serializationData.hasContents()) {
- // TODO - should we abstract out .dir serialization?
- IPath directoryPath = fileOrFolderPath.addFileExtension("dir");
- createFolder(project, directoryPath);
- createFile(project, directoryPath.append(serializationData.getNameHint()),
+ createFolder(project, serializationFolderPath);
+ createFile(project, serializationFolderPath.append(serializationData.getFileName()),
serializationData.getContents());
- // filter out the child of type Repository.NT_RESOURCE
- for (Iterator<ResourceProxy> it = resourceChildren.iterator(); it
- .hasNext();) {
+ // special processing for nt:resource nodes
+ for (Iterator<ResourceProxy> it = resourceChildren.iterator(); it.hasNext();) {
ResourceProxy child = it.next();
if (Repository.NT_RESOURCE.equals(child.getProperties().get(Repository.JCR_PRIMARY_TYPE))) {
@@ -233,12 +229,10 @@ public class ImportRepositoryContentActi
.size());
if (reloadedChildResource.getChildren().size() != 0) {
- // 1. create holder directory ; needs platform name format
String pathName = Text.getName(reloadedChildResource.getPath());
pathName = serializationManager.getOsPath(pathName);
-
- createFolder(project, directoryPath.append(pathName));
+ createFolder(project, serializationFolderPath.append(pathName));
// 2. recursively handle all resources
for (ResourceProxy grandChild : reloadedChildResource.getChildren()) {
@@ -256,12 +250,12 @@ public class ImportRepositoryContentActi
case FOLDER:
case METADATA_PARTIAL: {
- IFolder folder = createFolder(project, fileOrFolderPath);
+ IFolder folder = createFolder(project, serializationFolderPath);
parseIgnoreFiles(folder, path);
if (serializationData.hasContents()) {
- createFile(project, fileOrFolderPath.append(serializationData.getNameHint()),
+ createFile(project, serializationFolderPath.append(serializationData.getFileName()),
serializationData.getContents());
}
break;
@@ -269,7 +263,8 @@ public class ImportRepositoryContentActi
case METADATA_FULL: {
if (serializationData.hasContents()) {
- createFile(project, fileOrFolderPath, serializationData.getContents());
+ createFile(project, serializationFolderPath.append(serializationData.getFileName()),
+ serializationData.getContents());
}
break;
}
@@ -280,6 +275,8 @@ public class ImportRepositoryContentActi
if (serializationData.getSerializationKind() == SerializationKind.METADATA_FULL) {
return;
}
+ } else {
+ logger.warn("No serialization data found for " + resource.getPath());
}
ProgressUtils.advance(monitor, 1);
@@ -302,6 +299,23 @@ public class ImportRepositoryContentActi
}
}
+ /**
+ * Returns the path for serializing the nt:resource data of a nt:file node
+ *
+ * <p>
+ * The path will be one level above the <tt>serializationFolderPath</tt>, and the name will be the last path segment
+ * of the resource.
+ * </p>
+ *
+ * @param resource The resource
+ * @param serializationFolderPath the folder where the serialization data should be stored
+ * @return the path for the plain file node
+ */
+ private IPath getPathForPlainFileNode(ResourceProxy resource, IPath serializationFolderPath) {
+
+ return serializationFolderPath.removeLastSegments(1).append(Text.getName(resource.getPath()));
+ }
+
private void parseIgnoreFiles(IFolder folder, String path) throws IOException, CoreException {
// TODO - the parsing should be extracted
IResource vltIgnore = folder.findMember(".vltignore");
@@ -328,12 +342,6 @@ public class ImportRepositoryContentActi
return result.get();
}
- private void importFile(IProject project, IPath destinationPath, byte[] content)
- throws CoreException {
-
- createFile(project, destinationPath, content);
- }
-
private IFolder createFolder(IProject project, IPath destinationPath) throws CoreException {
IFolder destinationFolder = project.getFolder(destinationPath);
@@ -380,7 +388,7 @@ public class ImportRepositoryContentActi
}
createParents(container.getParent());
IFolder parentFolder = (IFolder)container;
- parentFolder.create(true, true, null);
+ parentFolder.create(true, true, null);
}
}
\ No newline at end of file
Modified: sling/trunk/tooling/ide/impl-vlt/src/org/apache/sling/ide/impl/vlt/serialization/VltSerializationDataBuilder.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/ide/impl-vlt/src/org/apache/sling/ide/impl/vlt/serialization/VltSerializationDataBuilder.java?rev=1605439&r1=1605438&r2=1605439&view=diff
==============================================================================
--- sling/trunk/tooling/ide/impl-vlt/src/org/apache/sling/ide/impl/vlt/serialization/VltSerializationDataBuilder.java (original)
+++ sling/trunk/tooling/ide/impl-vlt/src/org/apache/sling/ide/impl/vlt/serialization/VltSerializationDataBuilder.java Wed Jun 25 15:06:09 2014
@@ -20,14 +20,18 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.ListIterator;
import javax.jcr.Credentials;
import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
import javax.jcr.nodetype.NodeType;
import org.apache.jackrabbit.vault.fs.api.Aggregate;
@@ -104,99 +108,37 @@ public class VltSerializationDataBuilder
@Override
public SerializationData buildSerializationData(File contentSyncRoot, ResourceProxy resource) throws SerializationException {
- // TODO - there is a small mismatch here since we're doing remote calls to the repository
- // but taking a resourceProxy - not sure if we'll run into problems down the road or not
-
try {
- AggregateWrapper wrapper = findAggregate(resource);
+ List<Aggregate> chain = findAggregateChain(resource);
- if (wrapper == null || wrapper.aggregate == null) {
- //TODO: there are valid cases apparently when aggregate is null and yet there
- // are children which must be honored.. so we can't throw an exception here
- // but we should review why this aggregate is null here and if that's valid.
- System.err.println("No aggregate found for path " + resource.getPath());
+ if (chain == null) {
return null;
}
- String fileOrFolderPathHint;
- if (wrapper.parent == null) {
- fileOrFolderPathHint = PlatformNameFormat.getPlatformPath(wrapper.aggregate.getPath());
- } else {
- fileOrFolderPathHint = PlatformNameFormat.getPlatformPath(wrapper.parent.getPath()) + ".dir"
- + File.separatorChar + PlatformNameFormat.getPlatformPath(wrapper.aggregate.getRelPath());
- }
+ Aggregate aggregate = chain.get(chain.size() - 1);
- String nameHint = PlatformNameFormat.getPlatformName(wrapper.aggregate.getName());
+ String fileOrFolderPathHint = calculateFileOrFolderPathHint(chain);
- NodeType[] mixinNodeTypes = wrapper.aggregate.getNode().getMixinNodeTypes();
- List<String> mixinNodeTypeNames = new ArrayList<String>(mixinNodeTypes.length);
- for (NodeType nodeType : mixinNodeTypes)
- mixinNodeTypeNames.add(nodeType.getName());
+ String nameHint = PlatformNameFormat.getPlatformName(aggregate.getName());
- SerializationKind serializationKind = skm.getSerializationKind(wrapper.aggregate.getNode()
- .getPrimaryNodeType()
- .getName(), mixinNodeTypeNames);
+ SerializationKind serializationKind = getSerializationKind(aggregate);
if (resource.getPath().equals("/") || serializationKind == SerializationKind.METADATA_PARTIAL
|| serializationKind == SerializationKind.FILE || serializationKind == SerializationKind.FOLDER) {
nameHint = Constants.DOT_CONTENT_XML;
} else if (serializationKind == SerializationKind.METADATA_FULL) {
nameHint += ".xml";
- fileOrFolderPathHint += ".xml";
}
Activator.getDefault().getPluginLogger()
.trace("Got location {0} for path {1}", fileOrFolderPathHint, resource.getPath());
- Aggregator aggregator = fs.getAggregateManager().getAggregator(wrapper.aggregate.getNode(), null);
- if (aggregator instanceof FileAggregator) {
- // TODO - copy-pasted from FileAggregator, and really does not belong here...
- Node content = wrapper.aggregate.getNode();
- if (content.isNodeType(JcrConstants.NT_FILE)) {
- content = content.getNode(JcrConstants.JCR_CONTENT);
- }
- String mimeType = null;
- if (content.hasProperty(JcrConstants.JCR_MIMETYPE)) {
- try {
- mimeType = content.getProperty(JcrConstants.JCR_MIMETYPE).getString();
- } catch (RepositoryException e) {
- // ignore
- }
- }
- if (mimeType == null) {
- // guess mime type from name
- mimeType = MimeTypes.getMimeType(wrapper.aggregate.getNode().getName(),
- MimeTypes.APPLICATION_OCTET_STREAM);
- }
-
- boolean needsDir = !MimeTypes.matches(wrapper.aggregate.getNode().getName(), mimeType,
- MimeTypes.APPLICATION_OCTET_STREAM);
-
- if (!needsDir) {
- if (content.hasProperty(JcrConstants.JCR_MIXINTYPES)) {
- for (Value v : content.getProperty(JcrConstants.JCR_MIXINTYPES).getValues()) {
- if (!v.getString().equals(JcrConstants.MIX_LOCKABLE)) {
- needsDir = true;
- break;
- }
- }
- }
- }
-
- if (!needsDir) {
- return SerializationData.empty(fileOrFolderPathHint, serializationKind);
- }
- } else if (aggregator instanceof GenericAggregator) {
- // TODO - copy-pasted from GenericAggregator
- if (wrapper.aggregate.getNode().getPrimaryNodeType().getName().equals("nt:folder")
- && wrapper.aggregate.getNode().getMixinNodeTypes().length == 0) {
- return SerializationData.empty(fileOrFolderPathHint, serializationKind);
- }
+ if (!needsDir(aggregate)) {
+ return SerializationData.empty(fileOrFolderPathHint, serializationKind);
}
-
- DocViewSerializer s = new DocViewSerializer(wrapper.aggregate);
+ DocViewSerializer s = new DocViewSerializer(aggregate);
ByteArrayOutputStream out = new ByteArrayOutputStream();
s.writeContent(out);
@@ -211,7 +153,120 @@ public class VltSerializationDataBuilder
}
}
- private AggregateWrapper findAggregate(ResourceProxy resource) throws IOException, RepositoryException {
+ private SerializationKind getSerializationKind(Aggregate aggregate) throws RepositoryException {
+
+ NodeType[] mixinNodeTypes = aggregate.getNode().getMixinNodeTypes();
+ List<String> mixinNodeTypeNames = new ArrayList<String>(mixinNodeTypes.length);
+ for (NodeType nodeType : mixinNodeTypes)
+ mixinNodeTypeNames.add(nodeType.getName());
+
+ return skm.getSerializationKind(aggregate.getNode()
+ .getPrimaryNodeType()
+ .getName(), mixinNodeTypeNames);
+ }
+
+ private boolean needsDir(Aggregate aggregate) throws RepositoryException, PathNotFoundException,
+ ValueFormatException {
+
+ Aggregator aggregator = fs.getAggregateManager().getAggregator(aggregate.getNode(), null);
+ boolean needsDir = true;
+ if (aggregator instanceof FileAggregator) {
+ needsDir = false;
+ // TODO - copy-pasted from FileAggregator, and really does not belong here...
+ Node content = aggregate.getNode();
+ if (content.isNodeType(JcrConstants.NT_FILE)) {
+ content = content.getNode(JcrConstants.JCR_CONTENT);
+ }
+ String mimeType = null;
+ if (content.hasProperty(JcrConstants.JCR_MIMETYPE)) {
+ try {
+ mimeType = content.getProperty(JcrConstants.JCR_MIMETYPE).getString();
+ } catch (RepositoryException e) {
+ // ignore
+ }
+ }
+ if (mimeType == null) {
+ // guess mime type from name
+ mimeType = MimeTypes.getMimeType(aggregate.getNode().getName(),
+ MimeTypes.APPLICATION_OCTET_STREAM);
+ }
+
+ needsDir = !MimeTypes.matches(aggregate.getNode().getName(), mimeType,
+ MimeTypes.APPLICATION_OCTET_STREAM);
+
+ if (!needsDir) {
+ if (content.hasProperty(JcrConstants.JCR_MIXINTYPES)) {
+ for (Value v : content.getProperty(JcrConstants.JCR_MIXINTYPES).getValues()) {
+ if (!v.getString().equals(JcrConstants.MIX_LOCKABLE)) {
+ needsDir = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // TODO - copy-pasted from GenericAggregator
+ } else if (aggregator instanceof GenericAggregator) {
+ if (isPlainNtFolder(aggregate)) {
+ needsDir = false;
+ }
+ }
+ return needsDir;
+ }
+
+ private String calculateFileOrFolderPathHint(List<Aggregate> chain) throws RepositoryException {
+
+ ListIterator<Aggregate> aggs = chain.listIterator();
+ StringBuilder out = new StringBuilder();
+ while (aggs.hasNext()) {
+ Aggregate cur = aggs.next();
+ if (aggs.previousIndex() == 0) {
+ out.append(PlatformNameFormat.getPlatformPath(cur.getPath()));
+ } else {
+ out.append("/");
+ out.append(PlatformNameFormat.getPlatformPath(cur.getRelPath()));
+ }
+
+ if (needsDir(cur)) {
+ SerializationKind serializationKind = getSerializationKind(cur);
+
+ if (serializationKind == SerializationKind.FILE) {
+ out.append(".dir");
+ }
+
+ if (!aggs.hasNext() && serializationKind == SerializationKind.METADATA_FULL) {
+ out.delete(out.lastIndexOf("/"), out.length());
+ }
+ }
+ }
+
+ return out.toString();
+ }
+
+ private boolean isPlainNtFolder(Aggregate agg) throws RepositoryException {
+
+ return agg.getNode().getPrimaryNodeType().getName().equals("nt:folder")
+ && agg.getNode().getMixinNodeTypes().length == 0;
+ }
+
+ /**
+ * Returns the aggregates for a specific resource
+ *
+ * <p>
+ * In the simplest case, a single element is returned in the chain, signalling that the aggregate is a top-level
+ * one.
+ * </p>
+ *
+ * <p>
+ * For leaf aggregates, the list contains the top-most aggregates first and ends up with the leaf-most ones.
+ * </p>
+ *
+ * @param resource the resource to find the aggregate chain for
+ * @return a list of aggregates
+ * @throws IOException
+ * @throws RepositoryException
+ */
+ private List<Aggregate> findAggregateChain(ResourceProxy resource) throws IOException, RepositoryException {
VaultFile vaultFile = fs.getFile(resource.getPath());
String platformPath = resource.getPath();
@@ -228,49 +283,76 @@ public class VltSerializationDataBuilder
}
if (vaultFile == null) {
- // TODO proper logging ; discover if this is expected or not and fail hard if it's not
// this file might be a leaf aggregate of a vaultfile higher in the resource path ; so look for a
// parent higher
String parentPath = Text.getRelativeParent(resource.getPath(), 1);
while (!parentPath.equals("/")) {
- VaultFile parentFile = fs.getFile(parentPath);
- if (parentFile != null && parentFile.getAggregate() != null
- && parentFile.getAggregate().getLeaves() != null) {
- for (Aggregate leaf : parentFile.getAggregate().getLeaves()) {
- if (leaf.getPath().equals(resource.getPath())) {
+ VaultFile parentFile = fs.getFile(PlatformNameFormat.getPlatformPath(parentPath));
- return new AggregateWrapper(leaf, parentFile.getAggregate());
- }
+ if (parentFile != null) {
+ Aggregate parentAggregate = parentFile.getAggregate();
+ ArrayList<Aggregate> parents = new ArrayList<Aggregate>();
+ parents.add(parentAggregate);
+ List<Aggregate> chain = lookForAggregateInLeaves(resource, parentAggregate, parents);
+ if (chain != null) {
+ return chain;
}
}
parentPath = Text.getRelativeParent(parentPath, 1);
}
- System.err.println("No vaultFile at path " + resource.getPath());
return null;
}
}
- return new AggregateWrapper(vaultFile.getAggregate(), null);
+ return Collections.singletonList(vaultFile.getAggregate());
}
- public void setLocator(VaultFsLocator locator) {
-
- this.fsLocator = locator;
- }
+ /**
+ * Recursively looks for an aggregate matching the <tt>resource</tt>'s path starting at the <tt>parentAggregate</tt>
+ *
+ * <p>
+ * The returned chain will contain at least one aggregate, in case the resource is contained in a stand-alone (?)
+ * aggregate, or multiple aggregates in case the matching aggregate is a leaf one.
+ * </p>
+ *
+ * @param resource the resource
+ * @param parentAggregate the known parent aggregate which potentially matches this resource
+ * @param chain the chain used to record all intermediate aggregates
+ * @return the final aggregate chain
+ *
+ * @throws RepositoryException
+ */
+ private List<Aggregate> lookForAggregateInLeaves(ResourceProxy resource, Aggregate parentAggregate,
+ List<Aggregate> chain) throws RepositoryException {
- static class AggregateWrapper {
+ if (parentAggregate == null) {
+ return null;
+ }
- private AggregateWrapper(Aggregate aggregate, Aggregate parent) {
+ List<? extends Aggregate> leaves = parentAggregate.getLeaves();
+ if (leaves == null) {
+ return null;
+ }
- this.aggregate = aggregate;
- this.parent = parent;
+ for (Aggregate leaf : leaves) {
+ if (leaf.getPath().equals(resource.getPath())) {
+ chain.add(leaf);
+ return chain;
+ } else if (Text.isDescendant(leaf.getPath(), resource.getPath())) {
+ chain.add(leaf);
+ return lookForAggregateInLeaves(resource, leaf, chain);
+ }
}
- public Aggregate aggregate;
- public Aggregate parent;
+ return null;
+ }
+
+ public void setLocator(VaultFsLocator locator) {
+
+ this.fsLocator = locator;
}
}