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/10/10 00:05:15 UTC

svn commit: r1630602 - in /sling/trunk/tooling/ide: eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ eclipse-test/src/org/apache/sling/ide/test/impl/ eclipse-ui/src/org/apache/sling/ide/eclipse/ui/internal/

Author: rombert
Date: Thu Oct  9 22:05:14 2014
New Revision: 1630602

URL: http://svn.apache.org/r1630602
Log:
SLING-3573 - Local resources which are no longer present in the
repository are not deleted when importing

When importing, remove files not touched by the import action and which
fall in the scope of synchronization.

Added:
    sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ResourceAndInfo.java
Modified:
    sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ResourceChangeCommandFactory.java
    sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/ContentImportTest.java
    sling/trunk/tooling/ide/eclipse-ui/src/org/apache/sling/ide/eclipse/ui/internal/ImportRepositoryContentAction.java

Added: sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ResourceAndInfo.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ResourceAndInfo.java?rev=1630602&view=auto
==============================================================================
--- sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ResourceAndInfo.java (added)
+++ sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ResourceAndInfo.java Thu Oct  9 22:05:14 2014
@@ -0,0 +1,43 @@
+/*
+ * 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.ide.eclipse.core.internal;
+
+import org.apache.sling.ide.transport.FileInfo;
+import org.apache.sling.ide.transport.ResourceProxy;
+
+/**
+ * The <tt>ResourceAndInfo</tt> is a simple value class allowing both a ResourceProxy and a FileInfo to be passed
+ * together
+ *
+ */
+public class ResourceAndInfo {
+    private final ResourceProxy resource;
+    private final FileInfo info;
+
+    public ResourceAndInfo(ResourceProxy resource, FileInfo info) {
+        this.resource = resource;
+        this.info = info;
+    }
+
+    public ResourceProxy getResource() {
+        return resource;
+    }
+
+    public FileInfo getInfo() {
+        return info;
+    }
+}
\ No newline at end of file

Modified: sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ResourceChangeCommandFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ResourceChangeCommandFactory.java?rev=1630602&r1=1630601&r2=1630602&view=diff
==============================================================================
--- sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ResourceChangeCommandFactory.java (original)
+++ sling/trunk/tooling/ide/eclipse-core/src/org/apache/sling/ide/eclipse/core/internal/ResourceChangeCommandFactory.java Thu Oct  9 22:05:14 2014
@@ -92,14 +92,24 @@ public class ResourceChangeCommandFactor
 
         ResourceAndInfo rai = buildResourceAndInfo(resource, repository);
         
-        if ( rai == null ) {
+        if (rai == null) {
             return null;
         }
 
         return repository.newAddOrUpdateNodeCommand(rai.getInfo(), rai.getResource());
     }
 
-    private ResourceAndInfo buildResourceAndInfo(IResource resource, Repository repository) throws CoreException,
+    /**
+     * Convenience method which builds a <tt>ResourceAndInfo</tt> info for a specific <tt>IResource</tt>
+     * 
+     * @param resource the resource to process
+     * @param repository the repository, used to extract serialization information for different resource types
+     * @return the build object, or null if one could not be built
+     * @throws CoreException
+     * @throws SerializationException
+     * @throws IOException
+     */
+    public ResourceAndInfo buildResourceAndInfo(IResource resource, Repository repository) throws CoreException,
             SerializationException, IOException {
         if (ignoredFileNames.contains(resource.getName())) {
             return null;
@@ -552,22 +562,4 @@ public class ResourceChangeCommandFactor
                     + res, e));
         }
     }
-
-    private static class ResourceAndInfo {
-        private final ResourceProxy resource;
-        private final FileInfo info;
-
-        public ResourceAndInfo(ResourceProxy resource, FileInfo info) {
-            this.resource = resource;
-            this.info = info;
-        }
-
-        public ResourceProxy getResource() {
-            return resource;
-        }
-
-        public FileInfo getInfo() {
-            return info;
-        }
-    }
 }

Modified: sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/ContentImportTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/ContentImportTest.java?rev=1630602&r1=1630601&r2=1630602&view=diff
==============================================================================
--- sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/ContentImportTest.java (original)
+++ sling/trunk/tooling/ide/eclipse-test/src/org/apache/sling/ide/test/impl/ContentImportTest.java Thu Oct  9 22:05:14 2014
@@ -18,6 +18,8 @@ package org.apache.sling.ide.test.impl;
 
 import static org.apache.sling.ide.test.impl.helpers.EclipseResourceMatchers.hasFile;
 import static org.apache.sling.ide.test.impl.helpers.EclipseResourceMatchers.hasFolder;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.CoreMatchers.nullValue;
 import static org.junit.Assert.assertThat;
 
@@ -223,19 +225,21 @@ public class ContentImportTest {
         // install bundle facet
         project.installFacet("sling.content", "1.0");
 
-        wstServer.waitForServerToStart();
-
-        ServerAdapter server = new ServerAdapter(wstServer.getServer());
-        server.installModule(contentProject);
-
+        // create filter.xml
         project.createVltFilterWithRoots("/content/test-root");
 
         // create sling:Folder at /content/test-root
         project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/.content.xml"),
                 getClass().getResourceAsStream("sling-folder-nodetype.xml"));
 
+        wstServer.waitForServerToStart();
+
+        ServerAdapter server = new ServerAdapter(wstServer.getServer());
+        server.installModule(contentProject);
+
         // create server-side content
         RepositoryAccessor repo = new RepositoryAccessor(config);
+        repo.tryDeleteResource("/content/test-root");
         repo.createNode("/content/test-root", "sling:Folder");
         repo.createFile("/content/test-root/file.txt", "hello, world".getBytes());
         repo.doWithSession(new SessionRunnable<Void>() {
@@ -259,6 +263,9 @@ public class ContentImportTest {
             }
         });
         
+        assertThat(repo.getNode("/content/test-root").getPrimaryNodeType().getName(), equalTo("sling:Folder"));
+
+
         runImport(contentProject);
 
         assertThat("File not properly imported", contentProject,
@@ -273,7 +280,8 @@ public class ContentImportTest {
                 hasFolder("jcr_root/content/test-root/file.txt.dir/_jcr_content/firstChunk"));
         assertThat("First chunk properties not serialized", contentProject,
                 hasFile("jcr_root/content/test-root/file.txt.dir/_jcr_content/firstChunk/.content.xml"));
-
+        assertThat("Sling folder serialization file was deleted after import", contentProject,
+                hasFile("jcr_root/content/test-root/.content.xml"));
     }
 
     @Test
@@ -320,6 +328,100 @@ public class ContentImportTest {
                 hasFile("jcr_root/content/test-root/org.apache.sling.example.Component.config.xml"));
     }
 
+    @Test
+    public void importDeletesNoLongerPresentFile() throws Exception {
+
+        // create faceted project
+        IProject contentProject = projectRule.getProject();
+
+        ProjectAdapter project = new ProjectAdapter(contentProject);
+        project.addNatures(JavaCore.NATURE_ID, "org.eclipse.wst.common.project.facet.core.nature");
+
+        // install bundle facet
+        project.installFacet("sling.content", "1.0");
+
+        wstServer.waitForServerToStart();
+
+        ServerAdapter server = new ServerAdapter(wstServer.getServer());
+        server.installModule(contentProject);
+
+        project.createVltFilterWithRoots("/content/test-root");
+        project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/hello.txt"),
+                new ByteArrayInputStream("hello, world".getBytes()));
+
+        // create server-side content
+        RepositoryAccessor repo = new RepositoryAccessor(config);
+        repo.createNode("/content/test-root/en", "nt:folder");
+        repo.createNode("/content/test-root/en/files", "nt:folder");
+        repo.createFile("/content/test-root/en/files/first.txt", "first file".getBytes());
+        repo.createFile("/content/test-root/en/files/second.txt", "second file".getBytes());
+
+        // run initial import
+        runImport(contentProject);
+
+        // first.txt and second.txt should be present
+        assertThat(contentProject, hasFile("jcr_root/content/test-root/en/files/first.txt"));
+        assertThat(contentProject, hasFile("jcr_root/content/test-root/en/files/second.txt"));
+        // hello.txt is not present in the repo and should be deleted
+        assertThat(contentProject, not(hasFile("jcr_root/content/test-root/hello.txt")));
+
+    }
+    
+    @Test
+    public void importFromRepositoryWithNtUnstructuredNodeWithNoPropertiesCausesSpuriousLocalDeletion()
+            throws Exception {
+
+        RepositoryAccessor repo = new RepositoryAccessor(config);
+        repo.createNode("/content/test-root/en", "nt:folder");
+
+        // create faceted project
+        IProject contentProject = projectRule.getProject();
+
+        ProjectAdapter project = new ProjectAdapter(contentProject);
+        project.addNatures(JavaCore.NATURE_ID, "org.eclipse.wst.common.project.facet.core.nature");
+
+        // install bundle facet
+        project.installFacet("sling.content", "1.0");
+
+        wstServer.waitForServerToStart();
+
+        ServerAdapter server = new ServerAdapter(wstServer.getServer());
+        server.installModule(contentProject);
+
+        project.createVltFilterWithRoots("/content/test-root");
+        project.createOrUpdateFile(Path.fromPortableString("jcr_root/content/test-root/en/hello.txt"),
+                new ByteArrayInputStream("hello, world".getBytes()));
+        
+        repo.createNode("/content/test-root/folder", "sling:Folder");
+        repo.createNode("/content/test-root/folder/jcr:content", "nt:unstructured");
+        repo.createFile("/content/test-root/folder/jcr:content/some_file.txt", "dummy contents".getBytes());
+        repo.doWithSession(new SessionRunnable<Void>() {
+
+            @Override
+            public Void doWithSession(Session session) throws RepositoryException {
+                session.getRootNode().getNode("content/test-root/folder/jcr:content/some_file.txt/jcr:content")
+                        .setProperty("jcr:mimeType", "x-vendor-reserved");
+                session.save();
+
+                return null;
+            }
+        });
+
+        // run initial import
+        runImport(contentProject);
+
+        assertThat(contentProject,
+                hasFile("jcr_root/content/test-root/folder/_jcr_content/some_file.txt.dir/.content.xml"));
+
+        // run second import
+        runImport(contentProject);
+
+        assertThat(contentProject,
+                hasFile("jcr_root/content/test-root/folder/_jcr_content/some_file.txt.dir/.content.xml"));
+        
+    }
+    
+
     @Before
     public void setUp() throws Exception {
         RepositoryAccessor repo = new RepositoryAccessor(config);

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=1630602&r1=1630601&r2=1630602&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 Thu Oct  9 22:05:14 2014
@@ -21,9 +21,11 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.reflect.InvocationTargetException;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.util.Text;
@@ -31,6 +33,8 @@ import org.apache.sling.ide.eclipse.core
 import org.apache.sling.ide.eclipse.core.ProjectUtil;
 import org.apache.sling.ide.eclipse.core.ResourceUtil;
 import org.apache.sling.ide.eclipse.core.ServerUtil;
+import org.apache.sling.ide.eclipse.core.internal.ResourceAndInfo;
+import org.apache.sling.ide.eclipse.core.internal.ResourceChangeCommandFactory;
 import org.apache.sling.ide.eclipse.core.progress.ProgressUtils;
 import org.apache.sling.ide.filter.Filter;
 import org.apache.sling.ide.filter.FilterResult;
@@ -52,10 +56,14 @@ import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceVisitor;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
 import org.eclipse.wst.server.core.IServer;
 
 // intentionally does not implement IRunnableWithProgress to cut dependency on JFace
@@ -74,6 +82,8 @@ public class ImportRepositoryContentActi
     private Filter filter;
     private File contentSyncRoot;
     private IFolder contentSyncRootDir;
+    private Set<IResource> currentResources;
+    private IPath repositoryImportRoot;
 
     /**
      * @param server
@@ -89,6 +99,7 @@ public class ImportRepositoryContentActi
         this.project = project;
         this.serializationManager = serializationManager;
         this.ignoredResources = new IgnoredResources();
+        this.currentResources = new HashSet<IResource>();
     }
 
     public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException,
@@ -131,7 +142,7 @@ public class ImportRepositoryContentActi
         try {
 
             contentSyncRootDir = ProjectUtil.getSyncDirectory(project);
-            IPath repositoryImportRoot = projectRelativePath
+            repositoryImportRoot = projectRelativePath
                     .makeRelativeTo(contentSyncRootDir.getProjectRelativePath())
                     .makeAbsolute();
 
@@ -147,8 +158,16 @@ public class ImportRepositoryContentActi
                     .trace("Starting import; repository start point is {0}, workspace start point is {1}",
                             repositoryImportRoot, projectRelativePath);
 
+            recordNotIgnoredResources();
+
+            ProgressUtils.advance(monitor, 1);
+
             crawlChildrenAndImport(repositoryImportRoot.toPortableString());
 
+            removeNotIgnoredAndNotUpdatedResources(new NullProgressMonitor());
+
+            ProgressUtils.advance(monitor, 1);
+
         } catch (OperationCanceledException e) {
             throw e;
         } catch (Exception e) {
@@ -179,6 +198,67 @@ public class ImportRepositoryContentActi
 
     }
 
+    private void recordNotIgnoredResources() throws CoreException {
+
+        final ResourceChangeCommandFactory rccf = new ResourceChangeCommandFactory(serializationManager);
+
+        IResource importStartingPoint = contentSyncRootDir.findMember(repositoryImportRoot);
+        if (importStartingPoint == null) {
+            return;
+        }
+        importStartingPoint.accept(new IResourceVisitor() {
+
+            @Override
+            public boolean visit(IResource resource) throws CoreException {
+
+                try {
+                    ResourceAndInfo rai = rccf.buildResourceAndInfo(resource, repository);
+
+                    if (rai == null) {
+                        // can be a prerequisite
+                        return true;
+                    }
+
+                    String repositoryPath = rai.getResource().getPath();
+
+                    FilterResult filterResult = filter.filter(contentSyncRoot, repositoryPath);
+
+                    if (ignoredResources.isIgnored(repositoryPath)) {
+                        return false;
+                    }
+
+                    if (filterResult == FilterResult.ALLOW) {
+                        currentResources.add(resource);
+                        return true;
+                    }
+
+                    return false;
+                } catch (SerializationException e) {
+                    throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+                            "Failed reading current project's resources", e));
+                } catch (IOException e) {
+                    throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+                            "Failed reading current project's resources", e));
+                }
+            }
+        });
+
+        logger.trace("Found {0} not ignored local resources", currentResources.size());
+    }
+
+    private void removeNotIgnoredAndNotUpdatedResources(IProgressMonitor monitor) throws CoreException {
+
+        logger.trace("Found {0} resources to clean up", currentResources.size());
+
+        for (IResource resource : currentResources) {
+            if (resource.exists()) {
+                logger.trace("Deleting {0}", resource);
+                resource.delete(true, monitor);
+            }
+        }
+
+    }
+
     /**
      * Crawls the repository and recursively imports founds resources
      * @param path the current path to import from
@@ -352,9 +432,19 @@ public class ImportRepositoryContentActi
             destinationFolder.setSessionProperty(ResourceUtil.QN_IGNORE_NEXT_CHANGE, Boolean.TRUE.toString());
         }
 
+        removeTouchedResource(destinationFolder);
+
         return destinationFolder;
     }
 
+    private void removeTouchedResource(IResource resource) {
+
+        IResource current = resource;
+        do {
+            currentResources.remove(current);
+        } while ((current = current.getParent()) != null);
+    }
+
     private void createFile(IProject project, IPath path, byte[] node) throws CoreException {
         if (node==null) {
             throw new IllegalArgumentException("node must not be null");
@@ -375,6 +465,8 @@ public class ImportRepositoryContentActi
         	destinationFile.create(new ByteArrayInputStream(node), true, null);
         }
 
+        removeTouchedResource(destinationFile);
+
         destinationFile.setSessionProperty(ResourceUtil.QN_IGNORE_NEXT_CHANGE, Boolean.TRUE.toString());
     }