org.grails.ide.eclipse.commands.test.GrailsCommandUtilTest.java Source code

Java tutorial

Introduction

Here is the source code for org.grails.ide.eclipse.commands.test.GrailsCommandUtilTest.java

Source

/*******************************************************************************
 * Copyright (c) 2012 VMWare, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     VMWare, Inc. - initial API and implementation
 *******************************************************************************/
package org.grails.ide.eclipse.commands.test;

import static org.junit.Assert.assertArrayEquals;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.codehaus.jdt.groovy.model.GroovyNature;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.grails.ide.eclipse.commands.GrailsCommand;
import org.grails.ide.eclipse.commands.GrailsCommandFactory;
import org.grails.ide.eclipse.commands.GrailsCommandUtils;
import org.grails.ide.eclipse.commands.GroovyCompilerVersionCheck;
import org.grails.ide.eclipse.core.GrailsCoreActivator;
import org.grails.ide.eclipse.core.internal.classpath.PerProjectDependencyDataCache;
import org.grails.ide.eclipse.core.internal.classpath.SourceFolderJob;
import org.grails.ide.eclipse.core.model.GrailsVersion;
import org.grails.ide.eclipse.runtime.shared.DependencyData;
import org.grails.ide.eclipse.ui.internal.importfixes.GrailsProjectVersionFixer;
import org.grails.ide.eclipse.ui.internal.inplace.GrailsLaunchUtils;
import org.springsource.ide.eclipse.commons.core.ZipFileUtil;
import org.springsource.ide.eclipse.commons.frameworks.test.util.ACondition;
import org.springsource.ide.eclipse.commons.tests.util.StsTestUtil;

/**
 * Tests for (portions of) the {@link GrailsCommandUtils} class. 
 * @author Kris De Volder
 * @created 2010-08-04
 */
public class GrailsCommandUtilTest extends AbstractCommandTest {

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        GrailsProjectVersionFixer.testMode();
        GroovyCompilerVersionCheck.testMode();
        //Ensure prior test projects don't exist
        StsTestUtil.deleteAllProjects();
        //clearGrailsState(); //Workaround for https://jira.grails.org/browse/GRAILS-7655 (ivy cache corruption)
    }

    /**
     * This test will fail on the buildsite if proxy system properties are not what we expect.
     * This may indicate a problem with proxy settings in the build, or it may indicate that
     * proxy situation on the build server has changed. In the latter case, this test should
     * be updated.
     */
    // DISABLED!!!
    public void _testProxy() {
        //      if (StsTestUtil.isOnBuildSite()) {
        Map<String, String> props = GrailsCoreActivator.getDefault().getLaunchSystemProperties();
        //   assertProp(props, "http.proxyHost", "proxy.eng.vmware.com");
        //   assertProp(props, "http.proxyPort", "3128");
        //   assertProp(props, "https.proxyHost", "proxy.eng.vmware.com");
        //   assertProp(props, "https.proxyPort", "3128");
        //Proxy settings no longer required on build server and should *not* be set
        assertProp(props, "http.proxyHost", null);
        assertProp(props, "http.proxyPort", null);
        assertProp(props, "https.proxyHost", null);
        assertProp(props, "https.proxyPort", null);
        //      }
    }

    private void assertProp(Map<String, String> props, String k, String v) {
        assertEquals(k, v, props.get(k));
    }

    /**
     * Test to see if we can use the "eclipsifyProject" method to "eclipsify" a very bare-bones
     * imported grails project that is missing just about *everything* in the way of configuration files.
     * <p>
     * This bare bones project is created by deleting all Eclipse related stuff and the target directory
     * from a grails project and then importing this into eclipse as a "general" project. Thus it has the
     * minimum, of configuration to make it a valid Eclipse project but nothing more.
     */
    public void testEclipsifyBareBonesProject() throws Exception {
        GrailsVersion version = GrailsVersion.MOST_RECENT;
        if (GrailsVersion.MOST_RECENT.isSnapshot()) {
            //Don't run this for snapshot builds. Too much work to create test projects for moving target.
            return;
        }
        ensureDefaultGrailsVersion(version); // The version of the BareBones project
        URL bareBonesURL = getProjectZip("bareBonesProject", version);

        // Create bare bones project from zip file
        IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
        project = workspaceRoot.getProject("testProject");
        IPath unzipLoc = workspaceRoot.getLocation();
        ZipFileUtil.unzip(bareBonesURL, unzipLoc.toFile(), new NullProgressMonitor());
        project.create(new NullProgressMonitor());
        assertTrue(project.exists());
        project.open(new NullProgressMonitor());
        project.refreshLocal(IResource.DEPTH_INFINITE, null);
        assertTrue(project.isAccessible());
        assertTrue(project.isAccessible());
        assertTrue(project.getFolder("grails-app").exists());

        GrailsCommandUtils.eclipsifyProject(project);

        /////////////////////////////////////////////////////////////////////////////////////////////////
        //Check a few things about this test project

        assertTrue(project.hasNature(JavaCore.NATURE_ID)); // Should have Java Nature at this point
        assertTrue(GroovyNature.hasGroovyNature(project)); // Should also have Groovy nature

        IJavaProject javaProject = JavaCore.create(project);

        assertDefaultOutputFolder(javaProject);

        IClasspathEntry[] classPath = javaProject.getRawClasspath();
        for (IClasspathEntry classpathEntry : classPath) {
            System.out.println(kindString(classpathEntry.getEntryKind()) + ": " + classpathEntry.getPath());
        }

        //The usual source folders:
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "src/java", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "src/groovy", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/conf", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/controllers", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/domain", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/services", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/taglib", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "test/integration", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "test/unit", classPath);

        //The classpath containers for Java and Grails
        assertClassPathEntry(IClasspathEntry.CPE_CONTAINER, "org.eclipse.jdt.launching.JRE_CONTAINER", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_CONTAINER, "org.grails.ide.eclipse.core.CLASSPATH_CONTAINER",
                classPath);

        //Installed plugins source folders
        assertBareBonesProjectUserPluginSourceFolders(version);
        assertDefaultPluginSourceFolders(project);

        assertCharset(project);
    }

    /**
     * Asserts that all message.properties files have the proper charsets
      * @param project
     * @throws CoreException 
      */
    private void assertCharset(IProject project) throws CoreException {
        IFolder i18nFolder = project.getFolder("grails-app/i18n");
        internalAssertCharset(i18nFolder);
        i18nFolder = project.getFolder(".link_to_grails_plugins/feeds-1.5/grails-app/i18n");
        if (i18nFolder.exists()) {
            internalAssertCharset(i18nFolder);
        }
    }

    /**
     * @param i18nFolder
     * @throws CoreException
     */
    public void internalAssertCharset(IFolder i18nFolder) throws CoreException {
        assertEquals("Invalid charset encoding", "UTF-8", i18nFolder.getDefaultCharset(true));
        for (IResource member : i18nFolder.members()) {
            if (member.getType() == IResource.FILE) {
                assertEquals("Invalid charset encoding", "UTF-8", ((IFile) member).getCharset(true));
            } else {
                assertEquals("Invalid charset encoding", "UTF-8", ((IFolder) member).getDefaultCharset(true));
            }
        }
    }

    /**
    * Check some expected source folders for user installed plugins that are
    * contained in the 'barebones' zipped test project.
    */
    private void assertBareBonesProjectUserPluginSourceFolders(GrailsVersion version) throws JavaModelException {
        String feedsVersion;
        if (GrailsVersion.V_2_3_.compareTo(version) <= 0) {
            feedsVersion = "1.6";
        } else {
            feedsVersion = "1.5";
        }
        assertPluginSourceFolder(project, "feeds-" + feedsVersion, "grails-app", "taglib");
        assertPluginSourceFolder(project, "feeds-" + feedsVersion, "src", "groovy");
    }

    /**
     * Test for https://issuetracker.springsource.com/browse/STS-1348
     * <p>
     * Does eclipsifyProject work also if the project is linked rather than physically in the workspace.
     */
    public void testEclipsifyLinkedProject() throws Exception {
        GrailsVersion version = GrailsVersion.MOST_RECENT;
        if (GrailsVersion.MOST_RECENT.isSnapshot()) {
            //Don't run this for SS builds. Too much work to create test projects for moving target.
            return;
        }
        ensureDefaultGrailsVersion(version); // The version of the BareBones project
        URL bareBonesURL = getProjectZip("bareBonesProject", version);

        // Create bare bones project from zip file
        IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();

        File unzipLoc = StsTestUtil.createTempDirectory();
        ZipFileUtil.unzip(bareBonesURL, unzipLoc, new NullProgressMonitor());

        project = workspaceRoot.getProject("testProject");
        IProjectDescription desc = ResourcesPlugin.getWorkspace().newProjectDescription(project.getName());
        desc.setLocation(new Path(unzipLoc.toString()).append(project.getName()));
        project.create(desc, null);
        project.open(null);
        project.refreshLocal(IResource.DEPTH_INFINITE, null);

        assertTrue(project.isAccessible());
        assertTrue(project.getFolder("grails-app").exists());

        GrailsCommandUtils.eclipsifyProject(project);

        /////////////////////////////////////////////////////////////////////////////////////////////////
        //Check a few things about this test project
        checkProjectBasics();

        assertBareBonesProjectUserPluginSourceFolders(version);
    }

    private void checkProjectBasics() throws CoreException, JavaModelException {
        assertTrue(project.hasNature(JavaCore.NATURE_ID)); // Should have Java Nature at this point
        assertTrue(GroovyNature.hasGroovyNature(project)); // Should also have Groovy nature

        IJavaProject javaProject = JavaCore.create(project);

        assertDefaultOutputFolder(javaProject);

        IClasspathEntry[] classPath = javaProject.getRawClasspath();
        for (IClasspathEntry classpathEntry : classPath) {
            System.out.println(kindString(classpathEntry.getEntryKind()) + ": " + classpathEntry.getPath());
        }

        //The usual source folders:
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "src/java", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "src/groovy", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/conf", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/controllers", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/domain", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/services", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/taglib", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "test/integration", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "test/unit", classPath);

        //The classpath containers for Java and Grails
        assertClassPathEntry(IClasspathEntry.CPE_CONTAINER, "org.eclipse.jdt.launching.JRE_CONTAINER", classPath);
        assertClassPathEntry(IClasspathEntry.CPE_CONTAINER, "org.grails.ide.eclipse.core.CLASSPATH_CONTAINER",
                classPath);

        //Installed plugins source folders
        assertDefaultPluginSourceFolders(project);

        assertCharset(project);
    }

    public void testCreateAndEclipsifyLinkedProject() throws Exception {
        String projName = "test-linked-project";
        GrailsCommand createApp = GrailsCommandFactory.createApp(projName);
        final File projectLoc = new File(StsTestUtil.createTempDirectory(), projName);
        createApp.setPath(projectLoc.getParent());
        createApp.synchExec();
        GrailsCommandUtils.eclipsifyProject(null, new Path(projectLoc.getAbsolutePath()));

        project = ResourcesPlugin.getWorkspace().getRoot().getProject(projName);

        checkProjectBasics();
    }

    /**
     * This test isn't actually testing stuff from {@link GrailsCommandUtils}, but it is in here because
     * it uses some similar checks that a project is properly setup after doing a "raw" import of project
     * created outside eclipse using grails commands.
     * <p>
     * A "raw" import means simply pulling in a project from files from somewhere outside eclipse and doing 
     * nothing more than adding them as a new project to the workspace. As of Grails 1.3.5 this should produce
     * a correctly configured grails project (grails 1.3.5 produces correct .project etc. files). So there
     * should be no need to call 'eclipsify' on such projects.
     */
    public void testImportExistingProject() throws Throwable {
        GrailsProjectVersionFixer.globalAskToConvertToGrailsProjectAnswer = true;
        System.out.println(">>>>>>>>>>>>>>>>>>>testImportExistingProject");
        GroovyCompilerVersionCheck.testMode();
        ensureDefaultGrailsVersion(GrailsVersion.V_1_3_5); // GrailsVersion of test project!
        StsTestUtil.setAutoBuilding(true);
        System.out.println("Waiting for autobuild...");
        StsTestUtil.waitForAutoBuild();
        System.out.println("Done autobuild");

        URL bareBonesURL = this.getClass().getClassLoader().getResource("external-project.zip");

        String projectName = "External";
        // Create bare bones project from zip file
        IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();

        IPath unzipLoc = workspaceRoot.getLocation();
        ZipFileUtil.unzip(bareBonesURL, unzipLoc.toFile(), new NullProgressMonitor());

        project = workspaceRoot.getProject(projectName);
        project.create(null);

        if (!project.isOpen()) {
            project.open(null);
        }

        assertTrue(project.isAccessible());
        assertTrue(project.getFolder("grails-app").exists());

        /////////////////////////////////////////////////////////////////////////////////////////////////
        //Check a few things about this test project

        assertTrue(project.hasNature(JavaCore.NATURE_ID)); // Should have Java Nature at this point
        assertTrue(GroovyNature.hasGroovyNature(project)); // Should also have Groovy nature

        final IJavaProject javaProject = JavaCore.create(project);

        //Next we check the classpath related stuff. 
        // The classpath may not be right initially... but should eventually become correct as a background
        // refresh dependency job should get scheduled. 
        // ACondition
        new ACondition() {
            @Override
            public boolean test() throws Exception {
                System.out.println("Checking project config...");
                assertEquals("/" + project.getName() + "/target-eclipse/classes",
                        javaProject.getOutputLocation().toString());

                ///////////////////////////////////
                // Check resolved classpath stuff
                IClasspathEntry[] classPath = javaProject.getResolvedClasspath(false);

                // A whole bunch of libraries should be there, check for just a few of those

                // this one will fail on macs
                //            assertClassPathEntry(IClasspathEntry.CPE_LIBRARY, "jre/lib/rt.jar", classPath);
                assertClassPathEntry(IClasspathEntry.CPE_LIBRARY, "grails-core", classPath);
                assertClassPathEntry(IClasspathEntry.CPE_LIBRARY, "grails-bootstrap", classPath);
                assertClassPathEntry(IClasspathEntry.CPE_LIBRARY, "groovy-all", classPath);
                //assertClassPathEntry(IClasspathEntry.CPE_LIBRARY, "servlet-api", classPath);

                //            System.out.println(">>>Resolved classpath");
                //            for (IClasspathEntry entry : classPath) {
                //               System.out.println(kindString(entry.getEntryKind())+": "+entry.getPath());
                //            }
                //            System.out.println("<<<Resolved classpath");

                ///////////////////////////////////
                // Check raw classpath stuff
                classPath = javaProject.getRawClasspath();
                //            for (IClasspathEntry classpathEntry : classPath) {
                //               System.out.println(kindString(classpathEntry.getEntryKind())+": "+classpathEntry.getPath());
                //            }

                //The usual source folders:
                assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "src/java", classPath);
                assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "src/groovy", classPath);
                assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/conf", classPath);
                assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/controllers", classPath);
                assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/domain", classPath);
                assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/services", classPath);
                assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "grails-app/taglib", classPath);
                assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "test/integration", classPath);
                assertClassPathEntry(IClasspathEntry.CPE_SOURCE, "test/unit", classPath);

                //The classpath containers for Java and Grails
                assertClassPathEntry(IClasspathEntry.CPE_CONTAINER, "org.eclipse.jdt.launching.JRE_CONTAINER",
                        classPath);
                assertClassPathEntry(IClasspathEntry.CPE_CONTAINER,
                        "org.grails.ide.eclipse.core.CLASSPATH_CONTAINER", classPath);

                //Installed plugins source folders
                assertDefaultPluginSourceFolders(project);

                assertCharset(project);

                System.out.println("Checking project config => OK!");
                return true;
            }
        }.waitFor(60000);

        System.out.println("<<<<<<<<<<<<<<<<<<<<<<<<testImportExistingProject");
    }

    /**
     * User's can modify where the Grails stores the plugins for their project by setting a magic
     * variable inside their BuildConfig.groovy. This test verifies whether refresh dependencies properly
     * picks up on a changed plugin directory (change is reflected in the link).
     * @throws Exception 
     */
    public void testChangingGrailsProjectPluginsDir() throws Exception {
        project = ensureProject(this.getClass().getSimpleName());

        DependencyData data = PerProjectDependencyDataCache.get(project);
        String pluginsDir = data.getPluginsDirectory();

        assertPluginsLink(pluginsDir);

        File newPluginsDir = StsTestUtil.createTempDirectory();
        setBuildConfigVariable(project, "grails.project.plugins.dir", newPluginsDir.getCanonicalPath());

        GrailsCommandUtils.refreshDependencies(JavaCore.create(project), true);

        data = PerProjectDependencyDataCache.get(project);
        pluginsDir = data.getPluginsDirectory();
        assertEquals(
                "Editing BuildConfig.groovy to set 'grails.project.plugins.dir' didn't impact STS depenendency data",
                newPluginsDir.getCanonicalPath(), pluginsDir);
        assertPluginsLink(pluginsDir);
        assertCharset(project);
    }

    protected void setBuildConfigVariable(IProject project, String varName, String value)
            throws IOException, CoreException {
        IFile buildConfig = project.getFile("grails-app/conf/BuildConfig.groovy");
        File file = buildConfig.getLocation().toFile();
        String contents = FileUtils.readFileToString(file);
        int locationOfVar = contents.indexOf(varName + "=");
        if (locationOfVar >= 0) {
            // Try to replace existing def
            int endOfLine = contents.indexOf('\n', locationOfVar);
            contents = contents.substring(0, locationOfVar) + varDef(varName, value)
                    + contents.substring(endOfLine);
        } else {
            // Insert new def at start of file
            contents = varDef(varName, value) + "\n" + contents;
        }
        FileUtils.writeStringToFile(file, contents);
        buildConfig.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor());
    }

    /**
     * Relates to https://issuetracker.springsource.com/browse/STS-1832 and
     * https://issuetracker.springsource.com/browse/STS-1799
     * <p>
     * Spring security ACL plugin has a script that copies classes from the plugin
     * into the user's code. This causes duplicate class def in STS.
     * <p>
     * However, simply adding exclusions filters to the CP will cause
     * trouble for users who do not run the copying script.
     * @throws CoreException 
     */
    public void _testSpringSecurityACLClassPathExclusions() throws Exception {
        if (GrailsVersion.MOST_RECENT.equals(GrailsVersion.V_2_1_0_revisit)) {
            //This test is known to fail since Grails 2.0 RC1: still failing in Grails 2.0 final
            //See: https://jira.grails.org/browse/GRAILS-8198
            return;
        }

        ensureDefaultGrailsVersion(GrailsVersion.MOST_RECENT);
        project = ensureProject("ACLTest");
        final IJavaProject javaProject = JavaCore.create(project);

        //First try without running the script... there should be no exclusions in the CP

        System.out.println(">>> Installing plugin 'spring-security-acl' ...");
        System.out.println(GrailsCommandFactory.installPlugin(project, "spring-security-acl").synchExec());
        System.out.println("<<< Installing plugin 'spring-security-acl'");
        GrailsCommandUtils.refreshDependencies(javaProject, false);

        IClasspathEntry[] rawClassPath = javaProject.getRawClasspath();
        IClasspathEntry entry = assertPluginSourceFolder(rawClassPath, "spring-security-acl", "grails-app/domain");
        assertNoExclusions(entry);
        StsTestUtil.assertNoErrors(project);

        // Now try running the copying script...
        System.out.println(">>> Running 's2-create-acl-domains' command ...");
        System.out.println(GrailsCommand.forTest(project, "s2-create-acl-domains").synchExec());
        System.out.println("<<< Running 's2-create-acl-domains' command ");
        GrailsCommandUtils.refreshDependencies(javaProject, false);

        rawClassPath = javaProject.getRawClasspath();
        entry = assertPluginSourceFolder(rawClassPath, "spring-security-acl", "grails-app/domain");
        assertAclDomainExclusions(entry);
        StsTestUtil.assertNoErrors(project);
    }

    private void assertAclDomainExclusions(IClasspathEntry entry) {
        assertExclusions(entry, "**/AclClass.groovy", "**/AclEntry.groovy", "**/AclObjectIdentity.groovy",
                "**/AclSid.groovy");
    }

    /**
     * This test does the same as the one above, but it uses GrailsLaunchUtils to run the
     * grails command (the method called by the grails command prompt). 
     * <p>
     * Purpose of this test is to see that the launcher logic is doing the right thing
     * w.r.t. running refresh dependencies after executing the command.
     */
    public void _testSpringSecurityACLClassPathExclusionsLaunch() throws Exception {
        if (GrailsVersion.MOST_RECENT.equals(GrailsVersion.V_2_1_0_revisit)) {
            //This test is known to fail in RC1:
            //See: https://jira.grails.org/browse/GRAILS-8198
            return;
        }

        ensureDefaultGrailsVersion(GrailsVersion.MOST_RECENT);
        project = ensureProject("ACLTest2");
        final IJavaProject javaProject = JavaCore.create(project);

        //First try without running the script... there should be no exclusions in the CP

        System.out.println(">>> Installing plugin 'spring-security-acl' ...");
        System.out.println(GrailsCommandFactory.installPlugin(project, "spring-security-acl").synchExec());
        System.out.println("<<< Installing plugin 'spring-security-acl'");
        GrailsCommandUtils.refreshDependencies(javaProject, false);

        IClasspathEntry[] rawClassPath = javaProject.getRawClasspath();
        IClasspathEntry entry = assertPluginSourceFolder(rawClassPath, "spring-security-acl", "grails-app/domain");
        assertNoExclusions(entry);
        StsTestUtil.assertNoErrors(project);

        // Now try running the copying script...

        GrailsLaunchUtils.launch(javaProject, "s2-create-acl-domains");

        //The launch.. in this case is run asynchronously so we must wait a bit before we can hope the command
        //to finish and the refresh to run.
        new ACondition() {
            @Override
            public boolean test() throws Exception {
                IClasspathEntry[] rawClassPath = javaProject.getRawClasspath();
                IClasspathEntry entry = assertPluginSourceFolder(rawClassPath, "spring-security-acl",
                        "grails-app/domain");
                assertAclDomainExclusions(entry);
                return true;
            }
        }.waitFor(90000);
        StsTestUtil.assertNoErrors(project);
    }

    private void assertExclusions(IClasspathEntry entry, String... expecteds) {
        IPath[] actuals = entry.getExclusionPatterns();
        String[] actualStr = new String[actuals.length];
        for (int i = 0; i < actualStr.length; i++) {
            actualStr[i] = actuals[i].toString();
        }
        Arrays.sort(actualStr);
        Arrays.sort(expecteds);
        assertArrayEquals(expecteds, actualStr);
    }

    private void assertNoExclusions(IClasspathEntry entry) {
        IPath[] exclusions = entry.getExclusionPatterns();
        if (exclusions == null || exclusions.length == 0) {
            return; //OK
        } else { //Problem
            StringBuilder msg = new StringBuilder("Expected no exclusions but found:\n");
            for (IPath iPath : exclusions) {
                msg.append(iPath + "\n");
            }
            fail(msg.toString());
        }
    }

    private IClasspathEntry assertPluginSourceFolder(IClasspathEntry[] cp, String pluginName, String pathString) {
        for (IClasspathEntry e : cp) {
            if (e.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
                String path = e.getPath().toString();
                if (path.contains(pluginName) && path.endsWith(pathString)) {
                    return e;
                }
            }
        }
        fail("Couldn't find source folder '" + pathString + "' for plugin '" + pluginName);
        return null; // unreachable
    }

    private String varDef(String varName, String value) {
        value = value.replace("\\", "\\\\"); // Likely in windows paths
        //TODO: escape other stuff inside the value if that is needed 
        return varName + "=\"" + value + "\"";
    }

    private void assertPluginsLink(String pluginsDir) {
        IFolder link = project.getFolder(SourceFolderJob.PLUGINS_FOLDER_LINK);
        assertTrue("The " + SourceFolderJob.PLUGINS_FOLDER_LINK + " doesn't exist", link != null && link.exists());
        assertEquals(SourceFolderJob.PLUGINS_FOLDER_LINK + " points to wrong location", pluginsDir,
                link.getLocation().toString());
    }

}