499979: Deployment of nested plans fails 

Tentative fix:
- change ServerModuleDelegate to properly recurse into children
- change Server to properly compute IModuleResource(s) 
- add a new wizard for creating plan project and plan file

Task-Url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=499979
diff --git a/org.eclipse.virgo.ide.facet.core/META-INF/MANIFEST.MF b/org.eclipse.virgo.ide.facet.core/META-INF/MANIFEST.MF
index 3746670..af184ea 100644
--- a/org.eclipse.virgo.ide.facet.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.virgo.ide.facet.core/META-INF/MANIFEST.MF
@@ -31,3 +31,4 @@
 Bundle-ActivationPolicy: lazy
 Export-Package: org.eclipse.virgo.ide.facet.core
 Bundle-Localization: plugin
+Import-Package: org.eclipse.ui.ide.undo
diff --git a/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/core/AbstractOperation.java b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/core/AbstractOperation.java
new file mode 100644
index 0000000..cf79afb
--- /dev/null
+++ b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/core/AbstractOperation.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ *  Copyright (c) 2016 GianMaria Romanato
+ *  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
+ *  http://www.eclipse.org/legal/epl-v10.html
+ *
+ *  Contributors:
+ *     GianMaria Romanato - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.virgo.ide.facet.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * Base class for runnables used for creating/changing projects
+ */
+public abstract class AbstractOperation implements IWorkspaceRunnable {
+
+    public AbstractOperation() {
+        super();
+    }
+
+    protected String readResourceFromClassPath(String path, String charset) throws CoreException {
+        InputStream is = getClass().getResourceAsStream(path);
+        if (is != null) {
+            try {
+                InputStreamReader r = new InputStreamReader(is, charset);
+                StringBuilder sb = new StringBuilder();
+                int c;
+                while ((c = r.read()) != -1) {
+                    sb.append((char) c);
+                }
+                return sb.toString();
+            } catch (IOException e) {
+                throw new CoreException(new Status(IStatus.ERROR, FacetCorePlugin.PLUGIN_ID, e.getMessage(), e));
+            } finally {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        } else {
+            throw new CoreException(new Status(IStatus.ERROR, FacetCorePlugin.PLUGIN_ID, "Template file missing " + path)); //$NON-NLS-1$
+        }
+
+    }
+
+}
\ No newline at end of file
diff --git a/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/core/CreatePlanProjectOperation.java b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/core/CreatePlanProjectOperation.java
new file mode 100644
index 0000000..bb83491
--- /dev/null
+++ b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/core/CreatePlanProjectOperation.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ *  Copyright (c) 2016 GianMaria Romanato
+ *  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
+ *  http://www.eclipse.org/legal/epl-v10.html
+ *
+ *  Contributors:
+ *     GianMaria Romanato - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.virgo.ide.facet.core;
+
+import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.text.MessageFormat;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.core.commands.ExecutionException;
+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.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.Assert;
+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.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.launching.JavaRuntime;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.ide.undo.CreateProjectOperation;
+import org.eclipse.ui.ide.undo.WorkspaceUndoUtil;
+import org.eclipse.wst.common.project.facet.core.IFacetedProject;
+import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
+
+/**
+ * CreatePlanProjectOperation creates a new plan project. A plan project is a Java project with the plan facet. The
+ * operation optionally creates an empty plan file as well.
+ * <p>
+ */
+public class CreatePlanProjectOperation extends AbstractOperation {
+
+    private static final String UTF_8 = "UTF-8";
+
+    private static final String TEMPLATE_PATH = "/org/eclipse/virgo/ide/facet/internal/core/plan_template.xml";
+
+    private static final String WST_FACET_NATURE = org.eclipse.wst.common.project.facet.core.internal.FacetCorePlugin.PLUGIN_ID + ".nature"; //$NON-NLS-1$
+
+    private final IProject projectHandle;
+
+    private final URI location;
+
+    private final Shell shell;
+
+    private final boolean scoped;
+
+    private final boolean atomic;
+
+    private final String planName;
+
+    /**
+     * Creates a new instance
+     *
+     * @param projectHandle the handle to the project to be created
+     * @param location an optional location if the project is not to be created in the default location
+     * @param planName an optional plan name is a plan name is to be created within the project
+     * @param scoped whether the plan is scoped
+     * @param atomic whether the plan is atomic
+     * @param shell a shell for error reporting
+     */
+    public CreatePlanProjectOperation(IProject projectHandle, URI location, String planName, boolean scoped, boolean atomic, Shell shell) {
+        Assert.isNotNull(projectHandle, "projectHandle cannot be null"); //$NON-NLS-1$
+        Assert.isNotNull(shell, "shell cannot be null"); //$NON-NLS-1$
+        this.projectHandle = projectHandle;
+        this.location = location;
+        this.shell = shell;
+        this.scoped = scoped;
+        this.atomic = atomic;
+        this.planName = planName;
+    }
+
+    public void run(IProgressMonitor monitor) throws CoreException {
+        monitor.beginTask("", planName != null ? 4 : 3);
+
+        IWorkspace workspace = ResourcesPlugin.getWorkspace();
+        IProjectDescription description = workspace.newProjectDescription(projectHandle.getName());
+        description.setLocationURI(location);
+
+        // create the new project operation
+        CreateProjectOperation op = new CreateProjectOperation(description, "");
+        try {
+            op.execute(new NullProgressMonitor(), WorkspaceUndoUtil.getUIInfoAdapter(shell));
+        } catch (ExecutionException e1) {
+            CoreException cex = new CoreException(new Status(IStatus.ERROR, FacetCorePlugin.PLAN_FACET_ID, e1.getMessage()));
+            cex.initCause(e1);
+            throw cex;
+        }
+        monitor.worked(1);
+
+        // make it a Java/Plan project
+        description = projectHandle.getDescription();
+        String[] natures = description.getNatureIds();
+        String[] newNatures = new String[natures.length + 3];
+        System.arraycopy(natures, 0, newNatures, 0, natures.length);
+        System.arraycopy(new String[] { WST_FACET_NATURE, FacetCorePlugin.PLAN_NATURE_ID, JavaCore.NATURE_ID }, 0, newNatures, natures.length, 3);
+        description.setNatureIds(newNatures);
+        projectHandle.setDescription(description, null);
+
+        monitor.worked(1);
+
+        // setup classpath
+        IJavaProject javaProject = JavaCore.create(projectHandle);
+        IFolder binFolder = projectHandle.getFolder("bin");
+        binFolder.create(false, true, null);
+        javaProject.setOutputLocation(binFolder.getFullPath(), null);
+
+        IFolder sourceFolder = projectHandle.getFolder("src");
+        sourceFolder.create(false, true, null);
+        IClasspathEntry sourceEntry = JavaCore.newSourceEntry(sourceFolder.getFullPath());
+
+        List<IClasspathEntry> entries = Arrays.asList(sourceEntry, JavaRuntime.getDefaultJREContainerEntry());
+        javaProject.setRawClasspath(entries.toArray(new IClasspathEntry[entries.size()]), null);
+
+        IFacetedProject fProject = ProjectFacetsManager.create(projectHandle, true, null);
+        fProject.installProjectFacet(ProjectFacetsManager.getProjectFacet(FacetCorePlugin.PLAN_FACET_ID).getDefaultVersion(), null, null);
+
+        monitor.worked(1);
+
+        if (planName != null) {
+            String content = readResourceFromClassPath(TEMPLATE_PATH, UTF_8);
+            content = MessageFormat.format(content, planName, scoped, atomic);
+            IPath p = new Path(planName);
+            if (!"plan".equals(p.getFileExtension())) {
+                p = p.addFileExtension("plan");
+            }
+            IFile planFile = sourceFolder.getFile(p);
+            try {
+                planFile.create(new ByteArrayInputStream(content.getBytes(UTF_8)), true, null);
+            } catch (UnsupportedEncodingException e) {
+                throw new RuntimeException(e); // will never happen, all JVMs support UTF-8
+            }
+            monitor.worked(1);
+
+        }
+
+        monitor.done();
+    }
+
+}
diff --git a/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/core/FacetUtils.java b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/core/FacetUtils.java
index de17097..3662eba 100644
--- a/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/core/FacetUtils.java
+++ b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/core/FacetUtils.java
@@ -223,7 +223,7 @@
 
     /**
      * Gets all the plan files found in the given project.
-     * 
+     *
      * @param project
      * @return
      */
@@ -297,13 +297,16 @@
     }
 
     /**
-     * Returns the list of nested plans for a given plan file. The look only in the planFile project, unless the project
-     * is also a Java project. In such case, the project classpath is used to look for plans in required projects.
+     * Returns the list of nested plans for a given plan file, including the given plan file as the first one. The look
+     * only in the planFile project, unless the project is also a Java project. In such case, the project classpath is
+     * used to look for plans in required projects.
      *
-     * @param planFile
+     * @param planFile the plan file
+     * @param recurse whether the method should return only direct children of the file (false) or recurse and collect
+     *        all descendants (true)
      * @return
      */
-    public static List<IFile> getNestedPlanFiles(IFile planFile) {
+    public static List<IFile> getNestedPlanFiles(IFile planFile, boolean recurse) {
         if (!isPlanProject(planFile)) {
             return Collections.emptyList();
         }
@@ -312,7 +315,7 @@
         PlanReader reader = new PlanReader();
         Plan topLevelPlan;
         try {
-            topLevelPlan = reader.read(planFile.getContents());
+            topLevelPlan = reader.read(planFile);
         } catch (Exception e1) {
             return Collections.emptyList();
         }
@@ -351,7 +354,7 @@
                     // ignore self
                     if (!planFile.equals(iFile)) {
                         try {
-                            Plan p = reader.read(iFile.getContents());
+                            Plan p = reader.read(iFile);
                             PlanReference r = p.asRefence();
 
                             /*
@@ -402,7 +405,7 @@
                 nestedPlanFiles.add(nestedFile);
 
                 for (PlanReference aRef : nestedPlan.getNestedPlans()) {
-                    if (!alreadyProcessed.contains(aRef)) {
+                    if (recurse && !alreadyProcessed.contains(aRef)) {
                         toBeProcessed.add(aRef);
                     }
                 }
diff --git a/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/Artifact.java b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/Artifact.java
new file mode 100644
index 0000000..087b55e
--- /dev/null
+++ b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/Artifact.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ *  Copyright (c) 2016 GianMaria Romanato
+ *  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
+ *  http://www.eclipse.org/legal/epl-v10.html
+ *
+ *  Contributors:
+ *     GianMaria Romanato - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.virgo.ide.facet.internal.core;
+
+import org.osgi.framework.Version;
+
+/**
+ * Represents an artifact listed in a plan. Providers proper implementation of {@link #equals(Object)} and
+ * {@link #hashCode()}.
+ */
+public abstract class Artifact {
+
+    protected Artifact(String name, Version version) {
+        super();
+        this.name = name;
+        this.version = version;
+    }
+
+    protected final String name;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((name == null) ? 0 : name.hashCode());
+        result = prime * result + ((version == null) ? 0 : version.hashCode());
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        Artifact other = (Artifact) obj;
+        if (name == null) {
+            if (other.name != null) {
+                return false;
+            }
+        } else if (!name.equals(other.name)) {
+            return false;
+        }
+        if (version == null) {
+            if (other.version != null) {
+                return false;
+            }
+        } else if (!version.equals(other.version)) {
+            return false;
+        }
+        return true;
+    }
+
+    protected final Version version;
+
+    /**
+     * Gets the artifact name.
+     *
+     * @return
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Gets the artifact version.
+     *
+     * @return version or null if version was not specified.
+     */
+    public Version getVersion() {
+        return version;
+    }
+
+}
\ No newline at end of file
diff --git a/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/BundleReference.java b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/BundleReference.java
new file mode 100644
index 0000000..2656beb
--- /dev/null
+++ b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/BundleReference.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ *  Copyright (c) 2016 GianMaria Romanato
+ *  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
+ *  http://www.eclipse.org/legal/epl-v10.html
+ *
+ *  Contributors:
+ *     GianMaria Romanato - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.virgo.ide.facet.internal.core;
+
+import org.osgi.framework.Version;
+
+/**
+ * Represents a reference to a bundle.
+ */
+public class BundleReference extends Artifact {
+
+    /* package */ BundleReference(String name, Version version) {
+        super(name, version);
+    }
+}
diff --git a/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/Plan.java b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/Plan.java
index ee38b4e..a8641ff 100644
--- a/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/Plan.java
+++ b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/Plan.java
@@ -11,6 +11,7 @@
 
 package org.eclipse.virgo.ide.facet.internal.core;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import org.osgi.framework.Version;
@@ -22,19 +23,42 @@
  */
 public final class Plan extends PlanReference {
 
+    /**
+     * Gets this plan as a {@link PlanReference}. Useful for comparing this plan with a reference from another plan to
+     * identify whether such other bundle refers to this plan.
+     * 
+     * @return
+     */
     public PlanReference asRefence() {
         return new PlanReference(getName(), getVersion());
     }
 
-    private final List<PlanReference> nestedPlans;
+    private final List<Artifact> nestedArtifacts;
 
-    /* default */ Plan(String name, Version version, List<PlanReference> nestedPlans) {
+    /* default */ Plan(String name, Version version, List<Artifact> nestedArtifacts) {
         super(name, version);
-        this.nestedPlans = nestedPlans;
+        this.nestedArtifacts = nestedArtifacts;
     }
 
+    /**
+     * Returns the list of nested artifacts.
+     * @return
+     */
+    public List<Artifact> getNestedArtifacts() {
+        return nestedArtifacts;
+    }
+
+    /**
+     * A view over nested artifacts that returns plan references.
+     * @return
+     */
     public List<PlanReference> getNestedPlans() {
-        return nestedPlans;
+        List<PlanReference> refs = new ArrayList<PlanReference>();
+        for (Artifact a : nestedArtifacts) {
+            if (a instanceof PlanReference) {
+                refs.add((PlanReference) a);
+            }
+        }
+        return refs;
     }
-
 }
diff --git a/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/PlanReader.java b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/PlanReader.java
index 975e382..85fac12 100644
--- a/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/PlanReader.java
+++ b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/PlanReader.java
@@ -21,6 +21,7 @@
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
+import org.eclipse.core.resources.IFile;
 import org.osgi.framework.Version;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -57,17 +58,22 @@
 
     private static final String XSD_SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema"; //$NON-NLS-1$
 
+    private static final Object BUNDLE = "bundle"; //$NON-NLS-1$
+
     /**
      * Creates a {@link PlanDescriptor} meta-data artifact from an {@link InputStream}
      *
      * @param inputStream from which the plan is to be read
      * @return The plan descriptor (meta-data) from the input stream
      */
-    public Plan read(InputStream inputStream) {
+    public Plan read(IFile file) {
+        org.eclipse.core.runtime.Assert.isNotNull(file, "file cannot be null");
+        InputStream inputStream = null;
         try {
+            inputStream = file.getContents();
             Document doc = readDocument(inputStream);
             Element element = doc.getDocumentElement();
-            return parsePlanElement(element);
+            return parsePlanElement(file, element);
         } catch (Exception e) {
             throw new RuntimeException("Failed to read plan descriptor", e);
         } finally {
@@ -106,14 +112,14 @@
         return factory;
     }
 
-    private Plan parsePlanElement(Element element) {
+    private Plan parsePlanElement(IFile file, Element element) {
         String name = element.getAttribute(NAME_ATTRIBUTE);
         Version version = new Version(element.getAttribute(VERSION_ATTRIBUTE));
 
         Properties attributes = parseAttributes(element);
-        List<PlanReference> nestedPlans = parseNestedPlans(element.getElementsByTagName(ARTIFACT_ELEMENT), attributes);
+        List<Artifact> artifacts = parseNestedArtifacts(element.getElementsByTagName(ARTIFACT_ELEMENT), attributes);
 
-        return new Plan(name, version, nestedPlans);
+        return new Plan(name, version, artifacts);
     }
 
     private Properties parseAttributes(Element element) {
@@ -130,20 +136,23 @@
         return result;
     }
 
-    private List<PlanReference> parseNestedPlans(NodeList artifactElements, Properties attributes) {
-        List<PlanReference> refs = new ArrayList<PlanReference>();
+    private List<Artifact> parseNestedArtifacts(NodeList artifactElements, Properties attributes) {
+        List<Artifact> refs = new ArrayList<Artifact>();
         for (int i = 0; i < artifactElements.getLength(); i++) {
             Element artifactElement = (Element) artifactElements.item(i);
 
             String type = artifactElement.getAttribute(TYPE_ATTRIBUTE);
+            String name = artifactElement.getAttribute(NAME_ATTRIBUTE);
+            String versionString = artifactElement.getAttribute(VERSION_ATTRIBUTE);
+            Version version = null;
+            if (versionString != null && !versionString.isEmpty()) {
+                version = new Version(versionString);
+            }
+
             if (PLAN.equals(type)) {
-                String name = artifactElement.getAttribute(NAME_ATTRIBUTE);
-                String version = artifactElement.getAttribute(VERSION_ATTRIBUTE);
-                if (version != null && !version.isEmpty()) {
-                    refs.add(new PlanReference(name, new Version(version)));
-                } else {
-                    refs.add(new PlanReference(name, null));
-                }
+                refs.add(new PlanReference(name, version));
+            } else if (BUNDLE.equals(type)) {
+                refs.add(new BundleReference(name, version));
             }
         }
 
diff --git a/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/PlanReference.java b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/PlanReference.java
index f96bfb9..511b3e7 100644
--- a/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/PlanReference.java
+++ b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/PlanReference.java
@@ -17,62 +17,10 @@
  * Represents a reference to a nested plan. Provides a proper implementation of {@link #equals(Object)} and
  * {@link #hashCode()} and can be used in collections for structural equality.
  */
-public class PlanReference {
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((name == null) ? 0 : name.hashCode());
-        result = prime * result + ((version == null) ? 0 : version.hashCode());
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        PlanReference other = (PlanReference) obj;
-        if (name == null) {
-            if (other.name != null) {
-                return false;
-            }
-        } else if (!name.equals(other.name)) {
-            return false;
-        }
-        if (version == null) {
-            if (other.version != null) {
-                return false;
-            }
-        } else if (!version.equals(other.version)) {
-            return false;
-        }
-        return true;
-    }
+public class PlanReference extends Artifact {
 
     public PlanReference(String name, Version version) {
-        super();
-        this.name = name;
-        this.version = version;
-    }
-
-    protected final String name;
-
-    protected final Version version;
-
-    public String getName() {
-        return name;
-    }
-
-    public Version getVersion() {
-        return version;
+        super(name, version);
     }
 
 }
\ No newline at end of file
diff --git a/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/plan_template.xml b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/plan_template.xml
new file mode 100644
index 0000000..6f07463
--- /dev/null
+++ b/org.eclipse.virgo.ide.facet.core/src/org/eclipse/virgo/ide/facet/internal/core/plan_template.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plan name="{0}" version="1.0.0" scoped="{1}" atomic="{2}"
+	xmlns="http://www.springsource.org/schema/dm-server/plan" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="
+            http://www.springsource.org/schema/dm-server/plan 
+            http://www.springsource.org/schema/dm-server/plan/springsource-dm-server-plan.xsd">
+
+	<!-- <artifact type="plan" name="my.nested.plan" /> -->
+	<!-- <artifact type="bundle" name="my.bundle.symbolic.name" /> -->
+</plan> 
+                                                          
\ No newline at end of file
diff --git a/org.eclipse.virgo.ide.manifest.core/src/org/eclipse/virgo/ide/module/core/ServerModuleDelegate.java b/org.eclipse.virgo.ide.manifest.core/src/org/eclipse/virgo/ide/module/core/ServerModuleDelegate.java
index 2f3cfab..412cf1e 100644
--- a/org.eclipse.virgo.ide.manifest.core/src/org/eclipse/virgo/ide/module/core/ServerModuleDelegate.java
+++ b/org.eclipse.virgo.ide.manifest.core/src/org/eclipse/virgo/ide/module/core/ServerModuleDelegate.java
@@ -7,6 +7,7 @@
  *
  * Contributors:
  *     SpringSource, a division of VMware, Inc. - initial API and implementation
+ *     GianMaria Romanato - support for nested plans
  *******************************************************************************/
 
 package org.eclipse.virgo.ide.module.core;
@@ -82,11 +83,35 @@
 
     @Override
     public IModuleResource[] members() throws CoreException {
-        IPath moduleRelativePath = Path.EMPTY;
 
-        // Handle simple case of project being a bundle first
+        IModule module = getModule();
         final Set<IModuleResource> resources = new LinkedHashSet<IModuleResource>();
-        if (getModule().getModuleType().getId().equals(FacetCorePlugin.BUNDLE_FACET_ID)) {
+
+        /*
+         * Add recursion to collect nested elements. This method now returns as members of toplevel plans all nested
+         * plans and bundles collected via recursion.
+         *
+         * Originally it was returning only direct children and assuming they were bundles, which made it impossible for
+         * the tools to deploy nested plans.
+         *
+         * Note that when top level plans with nested plans are added to the virgo runtime, they are added as a tree of
+         * IModule objects. However, apparently WTP does not properly deal with arbitrary nesting of modules and just
+         * publishes resources for the top level modules and their direct children.
+         *
+         * As such to overcome this limitation the following code fools WTP by providing as resources of the top level
+         * plan all resources contained in nested plans and nested bundles, even if they are represented by different
+         * IModule instances in memory.
+         */
+        deepGetMembers(module, resources);
+
+        return resources.toArray(new IModuleResource[resources.size()]);
+    }
+
+    private void deepGetMembers(IModule module, final Set<IModuleResource> resources) throws CoreException, JavaModelException {
+        IPath moduleRelativePath = Path.EMPTY;
+        // Handle simple case of project being a bundle first
+
+        if (module.getModuleType().getId().equals(FacetCorePlugin.BUNDLE_FACET_ID)) {
             if (FacetUtils.hasProjectFacet(getProject(), FacetCorePlugin.WEB_FACET_ID)) {
                 IModule[] modules = ServerUtil.getModules(getProject());
                 for (IModule webModule : modules) {
@@ -99,7 +124,7 @@
             resources.addAll(getMembers(getProject(), moduleRelativePath));
         }
         // More complex handling of PAR and nested bundle project
-        else if (getModule().getModuleType().getId().equals(FacetCorePlugin.PAR_FACET_ID)) {
+        else if (module.getModuleType().getId().equals(FacetCorePlugin.PAR_FACET_ID)) {
 
             // Get the META-INF folder of the PAR first
             IResource metaInfFolder = getProject().findMember(BundleManifestCorePlugin.MANIFEST_FOLDER_NAME);
@@ -126,12 +151,12 @@
             }, IResource.DEPTH_ONE, false);
 
             // Iterate nested bundle projects
-            for (IModule module : getChildModules()) {
+            for (IModule childModule : getChildModules()) {
 
                 // Special handling of par nested wars with bundle nature
-                if (FacetUtils.hasProjectFacet(module.getProject(), FacetCorePlugin.WEB_FACET_ID)) {
-                    moduleRelativePath = new Path(module.getProject().getName() + ".war");
-                    ModuleDelegate delegate = (ModuleDelegate) module.loadAdapter(ModuleDelegate.class, null);
+                if (FacetUtils.hasProjectFacet(childModule.getProject(), FacetCorePlugin.WEB_FACET_ID)) {
+                    moduleRelativePath = new Path(childModule.getProject().getName() + ".war");
+                    ModuleDelegate delegate = (ModuleDelegate) childModule.loadAdapter(ModuleDelegate.class, null);
 
                     IModuleResource[] members = delegate.members();
                     for (IModuleResource member : members) {
@@ -143,38 +168,39 @@
                     }
                 }
                 // All other bundles project nested in a par
-                else if (FacetUtils.isBundleProject(module.getProject())) {
-                    String moduleFolderName = module.getProject().getName() + ".jar";
+                else if (FacetUtils.isBundleProject(childModule.getProject())) {
+                    String moduleFolderName = childModule.getProject().getName() + ".jar";
                     moduleRelativePath = new Path(moduleFolderName);
                     ModuleFolder folder = new ModuleFolder(null, moduleFolderName, Path.EMPTY);
-                    folder.setMembers(getMembers(module.getProject(), moduleRelativePath).toArray(new IModuleResource[0]));
+                    folder.setMembers(getMembers(childModule.getProject(), moduleRelativePath).toArray(new IModuleResource[0]));
                     resources.add(folder);
                 }
             }
         }
         // handling for plan projects
-        else if (getModule().getModuleType().getId().equals(FacetCorePlugin.PLAN_FACET_ID)) {
+        else if (module.getModuleType().getId().equals(FacetCorePlugin.PLAN_FACET_ID)) {
 
             // Get the plan file
-            String fileName = getModule().getId();
+            String fileName = module.getId();
             fileName = fileName.substring(fileName.indexOf(':') + 1);
             IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(fileName));
             if (!file.exists()) {
-                return resources.toArray(new IModuleResource[resources.size()]);
+                return;
             }
 
             ModuleFile planFile = new ModuleFile(file, file.getName(), moduleRelativePath);
             resources.add(planFile);
 
             // Iterate nested bundle projects
-            for (IModule module : getChildModules()) {
+            ModuleDelegate delegate0 = (ModuleDelegate) module.loadAdapter(ModuleDelegate.class, null);
+            for (IModule childModule : delegate0.getChildModules()) {
 
                 // Special handling of par nested wars with bundle nature
-                if (FacetUtils.hasProjectFacet(module.getProject(), FacetCorePlugin.WEB_FACET_ID)) {
-                    moduleRelativePath = new Path(module.getProject().getName() + ".war");
-                    ModuleDelegate delegate = (ModuleDelegate) module.loadAdapter(ModuleDelegate.class, null);
+                if (FacetUtils.hasProjectFacet(childModule.getProject(), FacetCorePlugin.WEB_FACET_ID)) {
+                    moduleRelativePath = new Path(childModule.getProject().getName() + ".war");
+                    ModuleDelegate delegate1 = (ModuleDelegate) childModule.loadAdapter(ModuleDelegate.class, null);
 
-                    IModuleResource[] members = delegate.members();
+                    IModuleResource[] members = delegate1.members();
                     for (IModuleResource member : members) {
                         if (member instanceof IModuleFile) {
                             resources.add(new ParModuleFile((IModuleFile) member, moduleRelativePath));
@@ -184,17 +210,28 @@
                     }
                 }
                 // All other bundles project nested in a par
-                else if (FacetUtils.isBundleProject(module.getProject())) {
-                    String moduleFolderName = module.getProject().getName() + ".jar";
+                else if (FacetUtils.isBundleProject(childModule.getProject())) {
+                    String moduleFolderName = childModule.getProject().getName() + ".jar";
                     moduleRelativePath = new Path(moduleFolderName);
                     ModuleFolder folder = new ModuleFolder(null, moduleFolderName, Path.EMPTY);
-                    folder.setMembers(getMembers(module.getProject(), moduleRelativePath).toArray(new IModuleResource[0]));
+                    folder.setMembers(getMembers(childModule.getProject(), moduleRelativePath).toArray(new IModuleResource[0]));
                     resources.add(folder);
-                } else if (FacetUtils.isParProject(module.getProject())) {
-                    moduleRelativePath = new Path(module.getProject().getName() + ".par");
-                    ModuleDelegate delegate = (ModuleDelegate) module.loadAdapter(ModuleDelegate.class, null);
+                } else if (FacetUtils.isPlanProject(childModule.getProject())) {
+                    // this new case makes sure the plan file itself is copied to the staging folder
+                    IFile file2 = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(childModule.getName()));
+                    ModuleFile mfile = new ModuleFile(file2, file2.getName(), Path.EMPTY);
+                    resources.add(mfile);
 
-                    IModuleResource[] members = delegate.members();
+                    // recursion to collect nested plans and bundles
+                    ModuleDelegate delegate3 = (ModuleDelegate) childModule.loadAdapter(ModuleDelegate.class, null);
+                    for (IModule aChild : delegate3.getChildModules()) {
+                        deepGetMembers(aChild, resources);
+                    }
+                } else if (FacetUtils.isParProject(childModule.getProject())) {
+                    moduleRelativePath = new Path(childModule.getProject().getName() + ".par");
+                    ModuleDelegate delegate2 = (ModuleDelegate) childModule.loadAdapter(ModuleDelegate.class, null);
+
+                    IModuleResource[] members = delegate2.members();
                     for (IModuleResource member : members) {
                         if (member instanceof IModuleFile) {
                             resources.add(new ParModuleFile((IModuleFile) member, moduleRelativePath));
@@ -204,9 +241,8 @@
                     }
                 }
             }
-        }
 
-        return resources.toArray(new IModuleResource[resources.size()]);
+        }
     }
 
     /**
@@ -474,13 +510,24 @@
         return true;
     }
 
-    public Set<IModule> getPlanDependencies(IFile file) {
+    private Set<IModule> getPlanDependencies(IFile file) {
         if (file == null || !file.exists()) {
             return Collections.emptySet();
         }
 
         Set<IModule> modules = new HashSet<IModule>();
 
+        /* add recursion to collect nested plans */
+        getPlanDependencies0(file, modules);
+
+        return modules;
+    }
+
+    private void getPlanDependencies0(IFile file, Set<IModule> modules) {
+        if (file == null || !file.exists()) {
+            return;
+        }
+
         try {
             DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
             Document doc = docBuilder.parse(file.getContents(true));
@@ -511,6 +558,21 @@
                             }
                         }
                     }
+                } else if ("plan".equals(type)) {
+                    List<IFile> nested = FacetUtils.getNestedPlanFiles(file, false);
+                    for (IFile iFile : nested) {
+                        IModule[] mmmm = ServerUtil.getModules(iFile.getProject());
+                        for (IModule iModule : mmmm) {
+                            String fileName = iModule.getId();
+                            fileName = fileName.substring(fileName.indexOf(':') + 1);
+                            IFile file2 = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(fileName));
+                            if (iFile.equals(file2)) {
+                                modules.add(iModule);
+                                break;
+                            }
+                        }
+
+                    }
                 } else if ("par".equals(type)) {
                     IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
                     for (IProject candidate : projects) {
@@ -540,6 +602,5 @@
                 new Status(IStatus.ERROR, "Problem while getting plan dependencies.", BundleManifestCorePlugin.PLUGIN_ID, e));
         }
 
-        return modules;
     }
 }
diff --git a/org.eclipse.virgo.ide.pde.core/src/org/eclipse/virgo/ide/pde/core/internal/cmd/SetupProjectOperation.java b/org.eclipse.virgo.ide.pde.core/src/org/eclipse/virgo/ide/pde/core/internal/cmd/SetupProjectOperation.java
index a763f74..8de05c1 100644
--- a/org.eclipse.virgo.ide.pde.core/src/org/eclipse/virgo/ide/pde/core/internal/cmd/SetupProjectOperation.java
+++ b/org.eclipse.virgo.ide.pde.core/src/org/eclipse/virgo/ide/pde/core/internal/cmd/SetupProjectOperation.java
@@ -1,10 +1,17 @@
+/*******************************************************************************
+ *  Copyright (c) 2015 GianMaria Romanato
+ *  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
+ *  http://www.eclipse.org/legal/epl-v10.html
+ *
+ *  Contributors:
+ *     GianMaria Romanato - initial API and implementation
+ *******************************************************************************/
 
 package org.eclipse.virgo.ide.pde.core.internal.cmd;
 
 import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.io.UnsupportedEncodingException;
 import java.text.MessageFormat;
 import java.util.ArrayList;
@@ -27,6 +34,7 @@
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.pde.core.project.IBundleProjectDescription;
 import org.eclipse.pde.core.project.IBundleProjectService;
+import org.eclipse.virgo.ide.facet.core.AbstractOperation;
 import org.eclipse.virgo.ide.facet.core.FacetCorePlugin;
 import org.eclipse.virgo.ide.pde.core.internal.Constants;
 import org.eclipse.wst.common.project.facet.core.IFacetedProject;
@@ -40,7 +48,7 @@
  * PDE project that has been modified after initial creation.
  * <p>
  */
-public class SetupProjectOperation implements IWorkspaceRunnable {
+public class SetupProjectOperation extends AbstractOperation implements IWorkspaceRunnable {
 
     private static final String WST_FACET_NATURE = org.eclipse.wst.common.project.facet.core.internal.FacetCorePlugin.PLUGIN_ID + ".nature"; //$NON-NLS-1$
 
@@ -187,31 +195,6 @@
         }
     }
 
-    private String readResourceFromClassPath(String path, String charset) throws CoreException {
-        InputStream is = getClass().getResourceAsStream(path);
-        if (is != null) {
-            try {
-                InputStreamReader r = new InputStreamReader(is, charset);
-                StringBuilder sb = new StringBuilder();
-                int c;
-                while ((c = r.read()) != -1) {
-                    sb.append((char) c);
-                }
-                return sb.toString();
-            } catch (IOException e) {
-                throw new CoreException(new Status(IStatus.ERROR, Constants.PLUGIN_ID, e.getMessage(), e));
-            } finally {
-                try {
-                    is.close();
-                } catch (IOException e) {
-                }
-            }
-        } else {
-            throw new CoreException(new Status(IStatus.ERROR, Constants.PLUGIN_ID, "Template file missing " + path)); //$NON-NLS-1$
-        }
-
-    }
-
     private IPath configureWABClasspath(IProject project) throws CoreException, JavaModelException {
         IJavaProject javaProject = (IJavaProject) project.getNature(JavaCore.NATURE_ID);
         javaProject.setOutputLocation(project.getFullPath().append(BIN), null);
diff --git a/org.eclipse.virgo.ide.runtime.core/src/org/eclipse/virgo/ide/runtime/internal/core/Server.java b/org.eclipse.virgo.ide.runtime.core/src/org/eclipse/virgo/ide/runtime/internal/core/Server.java
index 8f3481a..d4811d1 100644
--- a/org.eclipse.virgo.ide.runtime.core/src/org/eclipse/virgo/ide/runtime/internal/core/Server.java
+++ b/org.eclipse.virgo.ide.runtime.core/src/org/eclipse/virgo/ide/runtime/internal/core/Server.java
@@ -158,6 +158,17 @@
                     return planModule.getChildModules();
                 }
             }
+        } else if (FacetCorePlugin.PLAN_FACET_ID.equals(moduleType.getId())) {
+            /*
+             * To support nested plans now the tooling creates an IModule tree where top level plans have nested plans
+             * or bundles as children. WTP is passing back the path from the root to a given module as a parameter to
+             * this method to get the children, so here children are computed only for the last item in the list
+             * (module.length -1)
+             **/
+            ServerModuleDelegate planModule = (ServerModuleDelegate) module[module.length - 1].loadAdapter(ServerModuleDelegate.class, null);
+            if (planModule != null) {
+                return planModule.getChildModules();
+            }
         }
 
         return new IModule[0];
diff --git a/org.eclipse.virgo.ide.runtime.core/src/org/eclipse/virgo/ide/runtime/internal/core/command/JmxServerDeployCommand.java b/org.eclipse.virgo.ide.runtime.core/src/org/eclipse/virgo/ide/runtime/internal/core/command/JmxServerDeployCommand.java
index ac37675..6829e77 100644
--- a/org.eclipse.virgo.ide.runtime.core/src/org/eclipse/virgo/ide/runtime/internal/core/command/JmxServerDeployCommand.java
+++ b/org.eclipse.virgo.ide.runtime.core/src/org/eclipse/virgo/ide/runtime/internal/core/command/JmxServerDeployCommand.java
@@ -7,30 +7,23 @@
  *
  * Contributors:
  *     SpringSource, a division of VMware, Inc. - initial API and implementation
+ *     GianMaria Romanato - bug fixing
  *******************************************************************************/
 
 package org.eclipse.virgo.ide.runtime.internal.core.command;
 
-import java.io.File;
 import java.io.IOException;
 import java.net.URI;
-import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeoutException;
 
 import javax.management.openmbean.CompositeData;
 
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.ResourcesPlugin;
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.Path;
 import org.eclipse.virgo.ide.facet.core.FacetCorePlugin;
-import org.eclipse.virgo.ide.facet.core.FacetUtils;
 import org.eclipse.virgo.ide.runtime.core.IServerBehaviour;
 import org.eclipse.virgo.ide.runtime.core.ServerUtils;
 import org.eclipse.virgo.ide.runtime.internal.core.DeploymentIdentity;
 import org.eclipse.virgo.ide.runtime.internal.core.Server;
-import org.eclipse.virgo.util.io.FileCopyUtils;
 import org.eclipse.wst.server.core.IModule;
 
 /**
@@ -79,27 +72,6 @@
             }
         }
 
-        if (isPlan()) {
-            // plan module name is workspace-relative path
-            String path = module.getName();
-            IFile planFile = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(path));
-
-            List<IFile> files = FacetUtils.getNestedPlanFiles(planFile);
-            IPath stageDir = this.serverBehaviour.getServerDeployDirectory();
-            File stageFileDir = stageDir.toFile();
-
-            // make sure nested plans, if any, are copied to the stage dir so that
-            // they can be found
-            for (IFile iFile : files) {
-                File oldFile = new File(stageFileDir, iFile.getName());
-                if (oldFile.exists()) {
-                    oldFile.delete();
-                }
-                FileCopyUtils.copy(iFile.getLocation().toFile(), oldFile);
-            }
-
-        }
-
         CompositeData returnValue = doExecute();
         if (returnValue != null) {
             String symbolicName = (String) returnValue.get(ITEM_SYMBOLIC_NAME);
diff --git a/org.eclipse.virgo.ide.ui/plugin.properties b/org.eclipse.virgo.ide.ui/plugin.properties
index 9b18382..f0354a3 100644
--- a/org.eclipse.virgo.ide.ui/plugin.properties
+++ b/org.eclipse.virgo.ide.ui/plugin.properties
@@ -2,4 +2,7 @@
 providerName=Eclipse.org - Virgo

 

 pdeProjectWizard.name=PDE Bundle Project

-pdeProjectWizard.desc=Create a new PDE bundle project
\ No newline at end of file
+pdeProjectWizard.desc=Create a new PDE bundle project

+

+planProjectWizard.name=Plan Project

+planProjectWizard.desc=Create a new Plan project
\ No newline at end of file
diff --git a/org.eclipse.virgo.ide.ui/plugin.xml b/org.eclipse.virgo.ide.ui/plugin.xml
index 24febeb..13a5121 100644
--- a/org.eclipse.virgo.ide.ui/plugin.xml
+++ b/org.eclipse.virgo.ide.ui/plugin.xml
@@ -9,6 +9,7 @@
  * 
  * Contributors:
  *     SpringSource - initial API and implementation
+ *     GianMaria Romanato - new wizards and bugfixes
  *******************************************************************************/
 -->
 <plugin>
@@ -58,6 +59,20 @@
             %pdeProjectWizard.desc
          </description>
    </wizard>
+   <wizard
+         category="org.eclipse.virgo.ide.ui.category"
+         class="org.eclipse.virgo.ide.ui.wizards.NewPlanProjectWizard"
+         finalPerspective="org.eclipse.virgo.ide.runtime.ui.perspective"
+         hasPages="true"
+         icon="icons/full/obj16/par_project_obj.gif"
+         id="org.eclipse.virgo.ide.ui.planprojectwizard"
+         name="%planProjectWizard.name"
+         project="true">
+          <description>
+            %planProjectWizard.desc
+         </description>
+   </wizard>
+      
       
    </extension>
    
@@ -370,6 +385,9 @@
        </newWizardShortcut>
        <newWizardShortcut
              id="org.eclipse.virgo.ide.ui.pdeprojectwizard">
+       </newWizardShortcut>   
+       <newWizardShortcut
+             id="org.eclipse.virgo.ide.ui.planprojectwizard">
        </newWizardShortcut>       
        <newWizardShortcut
              id="org.eclipse.virgo.ide.ui.wizards.parProject">
@@ -558,6 +576,26 @@
 </or>      
             </enablement>
    </commonWizard>   
+   <commonWizard
+         type="new"
+         wizardId="org.eclipse.virgo.ide.ui.planprojectwizard">
+      <enablement>
+      <or>
+      <with 
+            variable="selection"> 
+         <count 
+               value="0"> 
+         </count> 
+      </with>
+      <instanceof
+            value="org.eclipse.core.resources.IProject">
+      </instanceof>
+      <instanceof
+            value="org.eclipse.core.resources.IWorkspaceRoot">
+      </instanceof> 
+</or>      
+            </enablement>
+   </commonWizard>   
 </extension>
 <extension
       point="org.eclipse.ui.views">
diff --git a/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/internal/actions/ConvertPlugInProject.java b/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/internal/actions/ConvertPlugInProject.java
index 7935133..3060f10 100644
--- a/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/internal/actions/ConvertPlugInProject.java
+++ b/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/internal/actions/ConvertPlugInProject.java
@@ -1,3 +1,13 @@
+/*******************************************************************************
+ *  Copyright (c) 2016 GianMaria Romanato
+ *  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
+ *  http://www.eclipse.org/legal/epl-v10.html
+ *
+ *  Contributors:
+ *     GianMaria Romanato - initial API and implementation
+ *******************************************************************************/
 
 package org.eclipse.virgo.ide.ui.internal.actions;
 
@@ -55,8 +65,7 @@
 
     @Override
     protected boolean showConfirmationDialog() {
-        return MessageDialog.openQuestion(this.part.getSite().getShell(), Messages.ConvertPlugInProject_title,
-            Messages.ConvertPlugInProject_message);
+        return MessageDialog.openQuestion(this.part.getSite().getShell(), Messages.ConvertPlugInProject_title, Messages.ConvertPlugInProject_message);
     }
 
 }
diff --git a/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/wizards/Messages.java b/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/wizards/Messages.java
index c5babfd..3006a53 100644
--- a/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/wizards/Messages.java
+++ b/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/wizards/Messages.java
@@ -45,6 +45,18 @@
 
     public static String NewPDEProjectWizard_title;
 
+    public static String NewPlanProjectFilePage_atomic_label;
+
+    public static String NewPlanProjectFilePage_scoped_label;
+
+    public static String NewPlanProjectNamePage_description;
+
+    public static String NewPlanProjectNamePage_plan_label;
+
+    public static String NewPlanProjectNamePage_title;
+
+    public static String NewPlanProjectWizard_title;
+
     public static String ProjectContentPageStrings_bundle_content;
 
     public static String ProjectContentPageStrings_bundle_content_desc;
diff --git a/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/wizards/NewPlanProjectFilePage.java b/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/wizards/NewPlanProjectFilePage.java
new file mode 100644
index 0000000..a07565f
--- /dev/null
+++ b/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/wizards/NewPlanProjectFilePage.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ *  Copyright (c) 2015 GianMaria Romanato
+ *  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
+ *  http://www.eclipse.org/legal/epl-v10.html
+ *
+ *  Contributors:
+ *     GianMaria Romanato - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.virgo.ide.ui.wizards;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+/**
+ * A wizard page for specifying a Web Bundle context root.
+ * <p />
+ *
+ */
+public class NewPlanProjectFilePage extends WizardPage {
+
+    /**
+     * The page name.
+     */
+    public static final String PAGE_NAME = NewPlanProjectFilePage.class.getSimpleName();
+
+    public NewPlanProjectFilePage() {
+        super(PAGE_NAME);
+        setTitle(Messages.NewPDEProjectWABPage_title);
+        setDescription(Messages.NewPDEProjectWABPage_description);
+    }
+
+    private String planName;
+
+    private Label planNameLabel;
+
+    private Text planNameText;
+
+    private Button scoped;
+
+    private Button atomic;
+
+    /**
+     * {@inheritDoc}
+     */
+    public void createControl(Composite parent) {
+        Composite container = new Composite(parent, SWT.NONE);
+        container.setLayout(new GridLayout());
+        createContextRootSection(container);
+        setControl(container);
+    }
+
+    /**
+     * Creates the context root text field.
+     *
+     * @param container the parent composite
+     */
+    protected void createContextRootSection(Composite container) {
+        container.setLayout(new GridLayout(3, false));
+        container.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+        this.planNameLabel = new Label(container, SWT.NONE);
+        this.planNameLabel.setText(Messages.NewPlanProjectNamePage_plan_label);
+
+        this.planNameText = new Text(container, SWT.BORDER | SWT.SINGLE);
+        GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).span(2, 1).applyTo(this.planNameText);
+
+        this.scoped = new Button(container, SWT.CHECK);
+        scoped.setText(Messages.NewPlanProjectFilePage_scoped_label);
+        scoped.setSelection(false);
+
+        this.atomic = new Button(container, SWT.CHECK);
+        atomic.setText(Messages.NewPlanProjectFilePage_atomic_label);
+        atomic.setSelection(true);
+
+        this.planNameText.addModifyListener(new ModifyListener() {
+
+            public void modifyText(ModifyEvent e) {
+                NewPlanProjectFilePage.this.modifyText(e);
+            }
+        });
+    }
+
+    /**
+     * Validates the context root.
+     *
+     * @param e the modify event fired by the text widget
+     */
+    protected void modifyText(ModifyEvent e) {
+        planName = null;
+
+        String name = planNameText.getText();
+        IStatus status = Status.OK_STATUS;// validator.validateContextRoot(name);
+
+        if (!status.isOK()) {
+            setPageComplete(false);
+            setErrorMessage(status.getMessage());
+            return;
+        }
+
+        planName = name;
+        setPageComplete(true);
+        setErrorMessage(null);
+    }
+
+    /**
+     * Returns the value of the plan name.
+     *
+     * @return the plan name
+     */
+    public String getPlanName() {
+        return planName;
+    }
+
+    public boolean isScoped() {
+        return scoped.getSelection();
+    }
+
+    public boolean isAtomic() {
+        return atomic.getSelection();
+    }
+
+}
diff --git a/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/wizards/NewPlanProjectWizard.java b/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/wizards/NewPlanProjectWizard.java
new file mode 100644
index 0000000..0f81125
--- /dev/null
+++ b/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/wizards/NewPlanProjectWizard.java
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ *  Copyright (c) 2016 GianMaria Romanato
+ *  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
+ *  http://www.eclipse.org/legal/epl-v10.html
+ *
+ *  Contributors:
+ *     GianMaria Romanato - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.virgo.ide.ui.wizards;
+
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.pde.internal.ui.wizards.NewWizard;
+import org.eclipse.ui.dialogs.WizardNewProjectCreationPage;
+import org.eclipse.ui.statushandlers.StatusManager;
+import org.eclipse.virgo.ide.facet.core.CreatePlanProjectOperation;
+import org.eclipse.virgo.ide.ui.ServerIdeUiPlugin;
+
+public class NewPlanProjectWizard extends NewWizard {
+
+    WizardNewProjectCreationPage mainPage;
+
+    private NewPlanProjectFilePage planPage;
+
+    public NewPlanProjectWizard() {
+        super();
+        setWindowTitle(Messages.NewPlanProjectWizard_title);
+        setDefaultPageImageDescriptor(ServerIdeUiPlugin.getImageDescriptor("full/wizban/wizban-par.png")); //$NON-NLS-1$
+        setNeedsProgressMonitor(true);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void addPages() {
+        super.addPages();
+        mainPage = new WizardNewProjectCreationPage("planPage");
+        mainPage.setTitle(Messages.NewPlanProjectNamePage_title);
+        mainPage.setDescription(Messages.NewPlanProjectNamePage_description);
+        addPage(mainPage);
+        planPage = new NewPlanProjectFilePage();
+        addPage(planPage);
+    }
+
+    @Override
+    public boolean performFinish() {
+
+        URI location = null;
+        if (!mainPage.useDefaults()) {
+            location = mainPage.getLocationURI();
+        }
+
+        String name = planPage.getPlanName();
+        boolean atomic = planPage.isAtomic();
+        boolean scoped = planPage.isScoped();
+
+        final CreatePlanProjectOperation operation = new CreatePlanProjectOperation(mainPage.getProjectHandle(), location, name, scoped, atomic,
+            getShell());
+
+        try {
+            getContainer().run(true, false, new IRunnableWithProgress() {
+
+                public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+                    try {
+                        ResourcesPlugin.getWorkspace().run(operation, monitor);
+                    } catch (CoreException e) {
+                        StatusManager.getManager().handle(e.getStatus(), StatusManager.LOG | StatusManager.SHOW);
+                    }
+                }
+            });
+        } catch (Exception e) {
+            IStatus s = new Status(IStatus.ERROR, ServerIdeUiPlugin.PLUGIN_ID, e.getMessage(), e);
+            StatusManager.getManager().handle(s, StatusManager.LOG | StatusManager.SHOW);
+        }
+
+        return true;
+    }
+
+}
diff --git a/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/wizards/messages.properties b/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/wizards/messages.properties
index 0152f53..32c0de3 100644
--- a/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/wizards/messages.properties
+++ b/org.eclipse.virgo.ide.ui/src/org/eclipse/virgo/ide/ui/wizards/messages.properties
@@ -17,6 +17,12 @@
 NewPDEProjectWABPage_description=Specify a Context root if you want the project to be a WAB
 NewPDEProjectWABPage_title=Web Application Bundle
 NewPDEProjectWizard_title=New Virgo PDE Bundle Project
+NewPlanProjectFilePage_atomic_label=Atomic
+NewPlanProjectFilePage_scoped_label=Scoped
+NewPlanProjectNamePage_description=Create a new Plan project
+NewPlanProjectNamePage_plan_label=Plan name
+NewPlanProjectNamePage_title=New Plan project
+NewPlanProjectWizard_title=New Plan Project
 ProjectContentPageStrings_bundle_content=Bundle Content
 ProjectContentPageStrings_bundle_content_desc=Enter the data required to generate the bundle.
 ProjectContentPageStrings_bundle_desc=Bundle Description