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