com.headwire.aem.tooling.intellij.communication.ImportRepositoryContentManager.java Source code

Java tutorial

Introduction

Here is the source code for com.headwire.aem.tooling.intellij.communication.ImportRepositoryContentManager.java

Source

/*
 * 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 com.headwire.aem.tooling.intellij.communication;

import com.headwire.aem.tooling.intellij.eclipse.ProjectUtil;
import com.headwire.aem.tooling.intellij.eclipse.ResourceAndInfo;
import com.headwire.aem.tooling.intellij.eclipse.ResourceChangeCommandFactory;
import com.headwire.aem.tooling.intellij.eclipse.ServerUtil;
import com.headwire.aem.tooling.intellij.eclipse.stub.CoreException;
import com.headwire.aem.tooling.intellij.eclipse.stub.IFile;
import com.headwire.aem.tooling.intellij.eclipse.stub.IFolder;
import com.headwire.aem.tooling.intellij.eclipse.stub.IPath;
import com.headwire.aem.tooling.intellij.eclipse.stub.IProgressMonitor;
import com.headwire.aem.tooling.intellij.eclipse.stub.IProject;
import com.headwire.aem.tooling.intellij.eclipse.stub.IResource;
import com.headwire.aem.tooling.intellij.eclipse.stub.IResourceVisitor;
import com.headwire.aem.tooling.intellij.eclipse.stub.IServer;
import com.headwire.aem.tooling.intellij.eclipse.stub.IStatus;
import com.headwire.aem.tooling.intellij.eclipse.stub.NullProgressMonitor;
import com.headwire.aem.tooling.intellij.eclipse.stub.ResourceUtil;
import com.headwire.aem.tooling.intellij.eclipse.stub.Status;
import org.apache.commons.io.IOUtils;
import org.apache.jackrabbit.vault.util.Text;
import org.apache.sling.ide.filter.Filter;
import org.apache.sling.ide.eclipse.core.internal.Activator;
import org.apache.sling.ide.filter.FilterResult;
import org.apache.sling.ide.filter.IgnoredResources;
import org.apache.sling.ide.log.Logger;
import org.apache.sling.ide.serialization.SerializationData;
import org.apache.sling.ide.serialization.SerializationDataBuilder;
import org.apache.sling.ide.serialization.SerializationException;
import org.apache.sling.ide.serialization.SerializationKind;
import org.apache.sling.ide.serialization.SerializationKindManager;
import org.apache.sling.ide.serialization.SerializationManager;
import org.apache.sling.ide.transport.Command;
import org.apache.sling.ide.transport.Repository;
import org.apache.sling.ide.transport.RepositoryException;
import org.apache.sling.ide.transport.ResourceProxy;
import org.apache.sling.ide.transport.Result;

import java.io.ByteArrayInputStream;
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 static com.headwire.aem.tooling.intellij.util.Constants.JCR_ROOT_FOLDER_NAME;

/**
 * This is an adaption of the org.apache.sling.ide.eclipse.ui.internal.ImportRepositoryContentAction class.
 *
 * Created by Andreas Schaefer (Headwire.com) on 6/17/15.
 */
public class ImportRepositoryContentManager {

    private final IServer server;
    private final IPath projectRelativePath;
    private final IProject project;
    private final Logger logger;

    private SerializationManager serializationManager;
    private SerializationDataBuilder builder;
    private IgnoredResources ignoredResources;
    private IProgressMonitor monitor;
    private Repository repository;
    private Filter filter;
    private File contentSyncRoot;
    private IFolder contentSyncRootDir;
    private Set<IResource> currentResources;
    private IPath repositoryImportRoot;

    /**
     * @param server
     * @param projectRelativePath
     * @param project
     * @throws SerializationException
     */
    public ImportRepositoryContentManager(IServer server, IPath projectRelativePath, IProject project,
            SerializationManager serializationManager) throws SerializationException {
        this.logger = Activator.getDefault().getPluginLogger();
        this.server = server;
        this.projectRelativePath = projectRelativePath;
        this.project = project;
        this.serializationManager = serializationManager;
        this.ignoredResources = new IgnoredResources();
        this.currentResources = new HashSet<IResource>();
    }

    public void doImport(IProgressMonitor monitor)
            throws InvocationTargetException, InterruptedException, SerializationException, CoreException {

        //        // TODO: We should try to make this give 'nice' progress feedback (aka here's what I'm processing)
        //        monitor.beginTask("Repository import", IProgressMonitor.UNKNOWN);

        //        this.monitor = monitor;

        MessageManager messageManager = project.getModule().getProject().getComponent(MessageManager.class);
        repository = ServerUtil.getConnectedRepository(server, monitor, messageManager);
        if (repository != null) {
            File syncDirectory = ProjectUtil.getSyncDirectoryFile(project);
            this.builder = serializationManager.newBuilder(repository, syncDirectory);

            SerializationKindManager skm;

            try {
                skm = new SerializationKindManager();
                skm.init(repository);
            } catch (RepositoryException e1) {
                throw new InvocationTargetException(e1);
            }

            filter = ProjectUtil.loadFilter(project);

            //        ProgressUtils.advance(monitor, 1);

            try {

                contentSyncRootDir = ProjectUtil.getSyncDirectory(project);
                //AS TODO: The Repository Import Root needs to be a path that points from the /jcr_root to the folder where the import started with.
                //            repositoryImportRoot = projectRelativePath
                //                .makeRelativeTo(contentSyncRootDir.getProjectRelativePath())
                //                .makeAbsolute();

                contentSyncRoot = ProjectUtil.getSyncDirectoryFullPath(project).toFile();

                String relativeFromSyncRoot = projectRelativePath.toOSString();
                int index = relativeFromSyncRoot.indexOf("/" + JCR_ROOT_FOLDER_NAME + "/");
                relativeFromSyncRoot = relativeFromSyncRoot
                        .substring(index + ("/" + JCR_ROOT_FOLDER_NAME + "/").length());
                repositoryImportRoot = new IPath(new IPath(contentSyncRoot), relativeFromSyncRoot);

                readVltIgnoresNotUnderImportRoot(contentSyncRootDir, repositoryImportRoot);

                //            ProgressUtils.advance(monitor, 1);

                Activator.getDefault().getPluginLogger().trace(
                        "Starting import; repository start point is {0}, workspace start point is {1}",
                        repositoryImportRoot, projectRelativePath);

                recordNotIgnoredResources();

                //            ProgressUtils.advance(monitor, 1);

                String contentPath = repositoryImportRoot.toPortableString();
                if (!contentPath.startsWith("/")) {
                    contentPath = "/" + contentPath;
                }
                crawlChildrenAndImport(contentPath);

                removeNotIgnoredAndNotUpdatedResources(new NullProgressMonitor());

                //            ProgressUtils.advance(monitor, 1);

                //        } catch (OperationCanceledException e) {
                //            throw e;
            } catch (Exception e) {
                throw new InvocationTargetException(e);
            } finally {
                if (builder != null) {
                    builder.destroy();
                    builder = null;
                }
                monitor.done();
            }
        }
    }

    private void readVltIgnoresNotUnderImportRoot(IFolder syncDir, IPath repositoryImportRoot)
            throws IOException, CoreException {

        IFolder current = syncDir;
        for (int i = 0; i < repositoryImportRoot.segmentCount(); i++) {
            IPath projectRelative = current.getProjectRelativePath();
            IPath syncDirProjectRelative = syncDir.getProjectRelativePath();
            IPath syncDirRelative = projectRelative.makeRelativeTo(syncDirProjectRelative);
            IPath repoPath = syncDirRelative.makeAbsolute();
            //            IPath repoPath = current.getProjectRelativePath().makeRelativeTo(syncDir.getProjectRelativePath())
            //                .makeAbsolute();
            parseIgnoreFiles(current, repoPath.toPortableString());
            current = (IFolder) current.findMember(repositoryImportRoot.segment(i));
        }

    }

    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);
                    //AS TODO: This is an adjustment to the 1.0.9 codebase
                    FilterResult filterResult = filter.filter(repositoryPath);

                    if (ignoredResources.isIgnored(repositoryPath)) {
                        return false;
                    }

                    if (filterResult == FilterResult.ALLOW) {
                        currentResources.add(resource);
                        return true;
                    }

                    return false;
                } catch (IOException e) {
                    throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
                            "Failed reading current project's resources", e));
                }
                //                throw new UnsupportedOperationException("This method needs to be implemented first");
            }
        });

        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
    //     * @param tracer
    //     * @throws JSONException
     * @throws RepositoryException
     * @throws CoreException
     * @throws IOException
     */
    // TODO: This probably should be pushed into the service layer
    private void crawlChildrenAndImport(String path)
            throws RepositoryException, CoreException, IOException, SerializationException {

        logger.trace("crawlChildrenAndImport({0},  {1}, {2}, {3}", repository, path, project, projectRelativePath);

        ResourceProxy resource = executeCommand(repository.newListChildrenNodeCommand(path));

        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 serializationFolderPath = contentSyncRootDir.getProjectRelativePath()
                    .append(serializationData.getFolderPath());

            switch (serializationData.getSerializationKind()) {
            case FILE: {
                byte[] contents = executeCommand(repository.newGetNodeCommand(path));
                createFile(project, getPathForPlainFileNode(resource, serializationFolderPath), contents);

                if (serializationData.hasContents()) {
                    createFolder(project, serializationFolderPath);
                    createFile(project, serializationFolderPath.append(serializationData.getFileName()),
                            serializationData.getContents());

                    // 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))) {

                            ResourceProxy reloadedChildResource = executeCommand(
                                    repository.newListChildrenNodeCommand(child.getPath()));

                            logger.trace(
                                    "Skipping direct handling of {0} node at {1} ; will additionally handle {2} direct children",
                                    Repository.NT_RESOURCE, child.getPath(),
                                    reloadedChildResource.getChildren().size());

                            if (reloadedChildResource.getChildren().size() != 0) {

                                String pathName = Text.getName(reloadedChildResource.getPath());
                                pathName = serializationManager.getOsPath(pathName);
                                createFolder(project, serializationFolderPath.append(pathName));

                                // 2. recursively handle all resources
                                for (ResourceProxy grandChild : reloadedChildResource.getChildren()) {
                                    crawlChildrenAndImport(grandChild.getPath());
                                }
                            }

                            it.remove();
                            break;
                        }
                    }
                }
                break;
            }
            case FOLDER:
            case METADATA_PARTIAL: {

                IFolder folder = createFolder(project, serializationFolderPath);

                parseIgnoreFiles(folder, path);

                if (serializationData.hasContents()) {
                    createFile(project, serializationFolderPath.append(serializationData.getFileName()),
                            serializationData.getContents());
                }
                break;
            }

            case METADATA_FULL: {
                if (serializationData.hasContents()) {
                    createFile(project, serializationFolderPath.append(serializationData.getFileName()),
                            serializationData.getContents());
                }
                break;
            }
            }

            logger.trace("Resource at {0} has children: {1}", resource.getPath(), resourceChildren);

            if (serializationData.getSerializationKind() == SerializationKind.METADATA_FULL) {
                return;
            }
        } else {
            logger.trace("No serialization data found for {0}", resource.getPath());
        }

        //        ProgressUtils.advance(monitor, 1);

        for (ResourceProxy child : resourceChildren) {

            if (ignoredResources.isIgnored(child.getPath())) {
                continue;
            }

            if (filter != null) {
                //                FilterResult filterResult = filter.filter(contentSyncRoot, child.getPath());
                //AS TODO: This is an adjustment to the 1.0.9 codebase
                FilterResult filterResult = filter.filter(child.getPath());
                if (filterResult == FilterResult.DENY) {
                    continue;
                }
            }

            crawlChildrenAndImport(child.getPath());
        }
    }

    /**
     * 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) {

        // TODO - can we just use the serializationFolderPath ?

        String name = serializationManager.getOsPath(Text.getName(resource.getPath()));

        return serializationFolderPath.removeLastSegments(1).append(name);
    }

    private void parseIgnoreFiles(IFolder folder, String path) throws IOException, CoreException {
        // TODO - the parsing should be extracted
        IResource vltIgnore = folder.findMember(".vltignore");
        if (vltIgnore != null && vltIgnore instanceof IFile) {

            logger.trace("Found ignore file at {0}", vltIgnore.getFullPath());

            InputStream contents = ((IFile) vltIgnore).getContents();
            try {
                List<String> ignoreLines = IOUtils.readLines(contents);
                for (String ignoreLine : ignoreLines) {
                    logger.trace("Registering ignore rule {0}:{1}", path, ignoreLine);
                    ignoredResources.registerRegExpIgnoreRule(path, ignoreLine);
                }
            } finally {
                IOUtils.closeQuietly(contents);
            }
        }
    }

    private <T> T executeCommand(Command<T> command) throws RepositoryException {

        Result<T> result = command.execute();
        return result.get();
    }

    private IFolder createFolder(IProject project, IPath destinationPath) throws CoreException {

        IFolder destinationFolder = project.getFolder(destinationPath);
        if (!destinationFolder.exists()) {
            logger.trace("Creating folder {0}", destinationFolder.getFullPath());

            createParents(destinationFolder.getParent());
            destinationFolder.create(true, true, null /* TODO progress monitor */);
        }

        destinationFolder.setSessionProperty(ResourceUtil.QN_IMPORT_MODIFICATION_TIMESTAMP,
                destinationFolder.getModificationStamp());

        removeTouchedResource(destinationFolder);

        return destinationFolder;
    }

    private void createParents(IResource container) throws CoreException {
        if (container.exists() || container.getType() != IResource.FOLDER) {
            return;
        }

        createParents((IFolder) container.getParent());
        createFolder(container.getProject(), container.getProjectRelativePath());
    }

    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");
        }

        IFile destinationFile = project.getFile(path.toOSString());

        logger.trace("Writing content file at {0}", path);

        if (destinationFile.exists()) {
            /* TODO progress monitor */
            destinationFile.setContents(new ByteArrayInputStream(node), IResource.KEEP_HISTORY, null);
        } else {
            /* TODO progress monitor */
            if (!destinationFile.getParent().exists()) {
                createParents(destinationFile.getParent());
            }
            destinationFile.create(new ByteArrayInputStream(node), true, null);
        }

        removeTouchedResource(destinationFile);

        destinationFile.setSessionProperty(ResourceUtil.QN_IMPORT_MODIFICATION_TIMESTAMP,
                destinationFile.getModificationStamp());
    }

}