[112729] Tomcat utility projects
diff --git a/plugins/org.eclipse.jst.server.core/sjavacore/org/eclipse/jst/server/core/PublishUtil.java b/plugins/org.eclipse.jst.server.core/sjavacore/org/eclipse/jst/server/core/PublishUtil.java
index fd2cc45..90543e4 100644
--- a/plugins/org.eclipse.jst.server.core/sjavacore/org/eclipse/jst/server/core/PublishUtil.java
+++ b/plugins/org.eclipse.jst.server.core/sjavacore/org/eclipse/jst/server/core/PublishUtil.java
@@ -11,6 +11,8 @@
 package org.eclipse.jst.server.core;
 
 import java.io.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.runtime.*;
@@ -47,7 +49,7 @@
 	private PublishUtil() {
 		super();
 	}
-	
+
 	/**
 	 * Copy a file from a to b. Closes the input stream after use.
 	 *
@@ -276,15 +278,48 @@
 		}
 	}
 
-	/*public static void createJar(IModuleResource[] resource, IPath jarPath) throws Exception {
-		ZipFile zip = new ZipFile(jarPath.toFile());
-		BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(jarPath.toFile()));
-		ZipOutputStream zout = new ZipOutputStream(bout);
-		
-		//ZipEntry ze = new ZipEntry();
-		//zout.putNextEntry(e);
-	}*/
+	public static void createZipFile(IModuleResource[] resources, IPath zipPath) throws CoreException {
+		try {
+			BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(zipPath.toFile()));
+			ZipOutputStream zout = new ZipOutputStream(bout);
+			
+			addZipEntries(zout, resources);
+			
+			zout.close();
+		} catch (Exception e) {
+			Trace.trace(Trace.SEVERE, "Error zipping", e);
+			throw new CoreException(new Status(IStatus.ERROR, JavaServerPlugin.PLUGIN_ID, 0, Messages.errorCopyingFile, e));
+		}
+	}
 
+	private static void addZipEntries(ZipOutputStream zout, IModuleResource[] resources) throws Exception {
+		int size = resources.length;
+		for (int i = 0; i < size; i++) {
+			if (resources[i] instanceof IModuleFolder) {
+				IModuleFolder mf = (IModuleFolder) resources[i];
+				IModuleResource[] res = mf.members();
+				addZipEntries(zout, res);
+				return;
+			}
+			
+			IModuleFile mf = (IModuleFile) resources[i];
+			IPath path = mf.getModuleRelativePath().append(mf.getName());
+			
+			ZipEntry ze = new ZipEntry(path.toPortableString());
+			zout.putNextEntry(ze);
+			
+			IFile file = (IFile) mf.getAdapter(IFile.class);
+			InputStream in = file.getContents();
+			int n = 0;
+			while (n > -1) {
+				n = in.read(buf);
+				if (n > 0)
+					zout.write(buf, 0, n);
+			}
+			
+			zout.closeEntry();
+		}
+	}
 	/**
 	 * Expand a zip file to a given directory.
 	 *
diff --git a/plugins/org.eclipse.jst.server.core/sjavacore/org/eclipse/jst/server/core/internal/J2EEUtil.java b/plugins/org.eclipse.jst.server.core/sjavacore/org/eclipse/jst/server/core/internal/J2EEUtil.java
index 9cc9b80..25d7de0 100644
--- a/plugins/org.eclipse.jst.server.core/sjavacore/org/eclipse/jst/server/core/internal/J2EEUtil.java
+++ b/plugins/org.eclipse.jst.server.core/sjavacore/org/eclipse/jst/server/core/internal/J2EEUtil.java
@@ -16,6 +16,7 @@
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.jst.server.core.IEnterpriseApplication;
 import org.eclipse.jst.server.core.IJ2EEModule;
+import org.eclipse.jst.server.core.IWebModule;
 import org.eclipse.wst.server.core.IModule;
 import org.eclipse.wst.server.core.ServerUtil;
 /**
@@ -23,10 +24,12 @@
  */
 public class J2EEUtil {
 	private static final String EAR_MODULE = "jst.ear";
+	private static final String WEB_MODULE = "jst.web";
+
 	/**
 	 * Returns the enterprise applications that the module is contained within.
 	 * 
-	 * @param module
+	 * @param module a J2EE module
 	 * @param monitor a progress monitor, or <code>null</code> if progress
 	 *    reporting and cancellation are not desired
 	 * @return a possibly empty array of enterprise applications
@@ -51,8 +54,77 @@
 				}
 			}
 		}
+		
 		IModule[] ears = new IModule[list.size()];
 		list.toArray(ears);
 		return ears;
 	}
+
+	/**
+	 * Returns the enterprise applications that the module is contained within.
+	 * 
+	 * @param module a J2EE module or utility module
+	 * @param monitor a progress monitor, or <code>null</code> if progress
+	 *    reporting and cancellation are not desired
+	 * @return a possibly empty array of enterprise applications
+	 */
+	public static IModule[] getEnterpriseApplications(IModule module, IProgressMonitor monitor) {
+		List list = new ArrayList();
+		IModule[] modules = ServerUtil.getModules(EAR_MODULE);
+		if (modules != null) {
+			int size = modules.length;
+			for (int i = 0; i < size; i++) {
+				IModule module2 = modules[i];
+				IEnterpriseApplication ear = (IEnterpriseApplication) module2.loadAdapter(IEnterpriseApplication.class, monitor);
+				if (ear != null) {
+					IModule[] modules2 = ear.getModules();
+					if (modules2 != null) {
+						int size2 = modules2.length;
+						for (int j = 0; j < size2; j++) {
+							if (module.equals(modules2[j]))
+								list.add(module2);
+						}
+					}
+				}
+			}
+		}
+		
+		IModule[] ears = new IModule[list.size()];
+		list.toArray(ears);
+		return ears;
+	}
+
+	/**
+	 * Returns the web modules that the utility module is contained within.
+	 * 
+	 * @param module a utility module
+	 * @param monitor a progress monitor, or <code>null</code> if progress
+	 *    reporting and cancellation are not desired
+	 * @return a possibly empty array of web modules
+	 */
+	public static IModule[] getWebModules(IModule module, IProgressMonitor monitor) {
+		List list = new ArrayList();
+		IModule[] modules = ServerUtil.getModules(WEB_MODULE);
+		if (modules != null) {
+			int size = modules.length;
+			for (int i = 0; i < size; i++) {
+				IModule module2 = modules[i];
+				IWebModule web = (IWebModule) module2.loadAdapter(IWebModule.class, monitor);
+				if (web != null) {
+					IModule[] modules2 = web.getModules();
+					if (modules2 != null) {
+						int size2 = modules2.length;
+						for (int j = 0; j < size2; j++) {
+							if (module.equals(modules2[j]))
+								list.add(module2);
+						}
+					}
+				}
+			}
+		}
+		
+		IModule[] webs = new IModule[list.size()];
+		list.toArray(webs);
+		return webs;
+	}
 }
\ No newline at end of file
diff --git a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/PublishOperation2.java b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/PublishOperation2.java
index c1bbba3..d6c3088 100644
--- a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/PublishOperation2.java
+++ b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/PublishOperation2.java
@@ -24,11 +24,11 @@
 
 public class PublishOperation2 extends PublishOperation {
 	protected TomcatServerBehaviour server;
-	protected IModule module;
+	protected IModule[] module;
 	protected int kind;
 	protected int deltaKind;
 
-	public PublishOperation2(TomcatServerBehaviour server, int kind, IModule module, int deltaKind) {
+	public PublishOperation2(TomcatServerBehaviour server, int kind, IModule[] module, int deltaKind) {
 		super("Publish to server", "Publish Web module to Tomcat server");
 		this.server = server;
 		this.module = module;
@@ -45,25 +45,62 @@
 	}
 
 	public void execute(IProgressMonitor monitor, IAdaptable info) throws CoreException {
+		if (module.length == 1) { // web module
+			publishDir(module[0], monitor);
+		} else { // utility module
+			publishJar(monitor);
+		}
+	}
+
+	private void publishDir(IModule module2, IProgressMonitor monitor) throws CoreException {
 		IPath path = server.getTempDirectory().append("webapps");
-		path = path.append(module.getName());
+		path = path.append(module2.getName());
 		
 		if (kind == IServer.PUBLISH_CLEAN) { // clean and republish from scratch
 			PublishUtil.deleteDirectory(path.toFile(), monitor);
 		}
 		
 		if (kind == IServer.PUBLISH_CLEAN || kind == IServer.PUBLISH_FULL) {
-			ProjectModule pm = (ProjectModule) module.loadAdapter(ProjectModule.class, monitor);
+			ProjectModule pm = (ProjectModule) module2.loadAdapter(ProjectModule.class, monitor);
 			IModuleResource[] mr = pm.members();
 			PublishUtil.copy(mr, path);
 			return;
 		}
 		
-		IModuleResourceDelta[] delta = server.getPublishedResourceDelta(new IModule[] { module });
+		IModuleResourceDelta[] delta = server.getPublishedResourceDelta(module);
 		
 		int size = delta.length;
 		for (int i = 0; i < size; i++) {
 			PublishUtil.handleDelta(kind, path, delta[i]);
 		}
 	}
+
+	private void publishJar(IProgressMonitor monitor) throws CoreException {
+		IModule module2 = null;
+		IPath path = server.getTempDirectory().append("webapps");
+		path = path.append(module[0].getName()).append("WEB-INF").append("lib").append(module[1].getName() + ".jar");
+		module2 = module[1];
+		
+		if (kind == IServer.PUBLISH_CLEAN) { // clean and republish from scratch
+			path.toFile().delete();
+		}
+		
+		ProjectModule pm = (ProjectModule) module2.loadAdapter(ProjectModule.class, monitor);
+		IModuleResource[] mr = pm.members();
+		PublishUtil.createZipFile(mr, path);
+		
+		/*if (kind == IServer.PUBLISH_CLEAN || kind == IServer.PUBLISH_FULL) {
+			ProjectModule pm = (ProjectModule) module2.loadAdapter(ProjectModule.class, monitor);
+			IModuleResource[] mr = pm.members();
+			PublishUtil.copy(mr, path);
+			return;
+		}*/
+		
+		/*IModuleResourceDelta[] delta = server.getPublishedResourceDelta(module);
+		
+		int size = delta.length;
+		for (int i = 0; i < size; i++) {
+			PublishUtil.handleDelta(kind, path, delta[i]);
+		}*/
+	}
 }
\ No newline at end of file
diff --git a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/PublishTask.java b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/PublishTask.java
index e08477f..4b3d556 100644
--- a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/PublishTask.java
+++ b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/PublishTask.java
@@ -30,9 +30,8 @@
 		int size = modules.size();
 		for (int i = 0; i < size; i++) {
 			IModule[] module = (IModule[]) modules.get(i);
-			IModule m = module[module.length - 1];
 			Integer in = (Integer) kindList.get(i);
-			tasks.add(new PublishOperation2(tomcatServer, kind, m, in.intValue()));
+			tasks.add(new PublishOperation2(tomcatServer, kind, module, in.intValue()));
 		}
 		
 		return (PublishOperation[]) tasks.toArray(new PublishOperation[tasks.size()]);
diff --git a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatServer.java b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatServer.java
index 352bad0..0cee0da 100644
--- a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatServer.java
+++ b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatServer.java
@@ -16,6 +16,7 @@
 import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.runtime.*;
 import org.eclipse.jst.server.core.IWebModule;
+import org.eclipse.jst.server.core.internal.J2EEUtil;
 import org.eclipse.osgi.util.NLS;
 
 import org.eclipse.wst.server.core.*;
@@ -210,12 +211,13 @@
 		if (module == null)
 			return null;
 		
-		if (module.length == 1) {
+		if (module.length == 1 && "jst.web".equals(module[0].getModuleType().getId())) {
 			IWebModule webModule = (IWebModule) module[0].loadAdapter(IWebModule.class, null);
 			if (webModule != null) {
-				/*IModule[] modules = webModule.getModules();
-				if (modules != null)
-					System.out.println(modules.length);*/
+				IModule[] modules = webModule.getModules();
+				//if (modules != null)
+				//	System.out.println(modules.length);
+				return modules;
 			}
 		}
 		return new IModule[0];
@@ -225,13 +227,14 @@
 	 * Returns the root module(s) of this module.
 	 */
 	public IModule[] getRootModules(IModule module) throws CoreException {
-		if (module.loadAdapter(IWebModule.class, null) != null) {
+		if ("jst.web".equals(module.getModuleType().getId())) {
 			IStatus status = canModifyModules(new IModule[] { module }, null);
 			if (status == null || !status.isOK())
 				throw new CoreException(status);
 			return new IModule[] { module };
 		}
-		return null;
+		
+		return J2EEUtil.getWebModules(module, null);
 	}
 
 	/**
diff --git a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatServerBehaviour.java b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatServerBehaviour.java
index e2f2a39..16e96a6 100644
--- a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatServerBehaviour.java
+++ b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatServerBehaviour.java
@@ -25,7 +25,6 @@
 import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
 import org.eclipse.jdt.launching.IVMInstall;
 import org.eclipse.jdt.launching.JavaRuntime;
-import org.eclipse.jst.server.core.IWebModule;
 import org.eclipse.jst.server.core.PublishUtil;
 import org.eclipse.osgi.util.NLS;
 
@@ -243,8 +242,30 @@
 			// ignore
 		}
 		
-		IModule module = moduleTree[0];
+		if (moduleTree.length == 1) // web module
+			publishDir(deltaKind, p, moduleTree[0], monitor);
+		else // utility jar
+			publishJar(deltaKind, p, moduleTree, monitor);
 		
+		setModulePublishState(moduleTree, IServer.PUBLISH_STATE_NONE);
+		
+		try {
+			p.store(new FileOutputStream(path.toFile()), "Tomcat publish data");
+		} catch (Exception e) {
+			// ignore
+		}
+	}
+
+	/**
+	 * Publish a web module.
+	 * 
+	 * @param deltaKind
+	 * @param p
+	 * @param module
+	 * @param monitor
+	 * @throws CoreException
+	 */
+	private void publishDir(int deltaKind, Properties p, IModule module, IProgressMonitor monitor) throws CoreException {
 		if (deltaKind == REMOVED) {
 			try {
 				String publishPath = (String) p.get(module.getId());
@@ -253,21 +274,41 @@
 				throw new CoreException(new Status(IStatus.WARNING, TomcatPlugin.PLUGIN_ID, 0, "Could not remove module", e));
 			}
 		} else {
-			IWebModule webModule = (IWebModule) module.loadAdapter(IWebModule.class, null);
-			IPath to = getServer().getRuntime().getLocation().append("webapps").append(webModule.getContextRoot());
+			IPath to = getServer().getRuntime().getLocation().append("webapps").append(module.getName());
 			
 			ProjectModule pm = (ProjectModule) module.loadAdapter(ProjectModule.class, monitor);
 			IModuleResource[] mr = pm.members();
 			PublishUtil.smartCopy(mr, to, monitor);
 			p.put(module.getId(), to.toOSString());
-			
-			setModulePublishState(moduleTree, IServer.PUBLISH_STATE_NONE);
 		}
+	}
+
+	/**
+	 * Publish a jar file.
+	 * 
+	 * @param deltaKind
+	 * @param p
+	 * @param module
+	 * @param monitor
+	 * @throws CoreException
+	 */
+	private void publishJar(int deltaKind, Properties p, IModule[] module, IProgressMonitor monitor) throws CoreException {
 		
-		try {
-			p.store(new FileOutputStream(path.toFile()), "Tomcat publish data");
-		} catch (Exception e) {
-			// ignore
+		if (deltaKind == REMOVED) {
+			try {
+				String publishPath = (String) p.get(module[1].getId());
+				new File(publishPath).delete();
+			} catch (Exception e) {
+				throw new CoreException(new Status(IStatus.WARNING, TomcatPlugin.PLUGIN_ID, 0, "Could not remove module", e));
+			}
+		} else {
+			IPath path = getServer().getRuntime().getLocation().append("webapps").append(module[0].getName());
+			path = path.append("WEB-INF").append("lib").append(module[1].getName() + ".jar");
+			
+			ProjectModule pm = (ProjectModule) module[1].loadAdapter(ProjectModule.class, monitor);
+			IModuleResource[] mr = pm.members();
+			PublishUtil.createZipFile(mr, path);
+			p.put(module[1].getId(), path.toOSString());
 		}
 	}
 
diff --git a/plugins/org.eclipse.jst.server.tomcat.ui/META-INF/MANIFEST.MF b/plugins/org.eclipse.jst.server.tomcat.ui/META-INF/MANIFEST.MF
index 75c8d8b..6b27daa 100644
--- a/plugins/org.eclipse.jst.server.tomcat.ui/META-INF/MANIFEST.MF
+++ b/plugins/org.eclipse.jst.server.tomcat.ui/META-INF/MANIFEST.MF
@@ -9,6 +9,7 @@
 Export-Package: org.eclipse.jst.server.tomcat.ui.internal;x-internal:=true,
  org.eclipse.jst.server.tomcat.ui.internal.editor;x-internal:=true
 Require-Bundle: org.eclipse.core.runtime,
+ org.eclipse.core.resources,
  org.eclipse.core.expressions,
  org.eclipse.ui,
  org.eclipse.ui.ide,
diff --git a/plugins/org.eclipse.jst.server.tomcat.ui/tomcatui/org/eclipse/jst/server/tomcat/ui/internal/editor/ConfigurationWebModuleEditorPart.java b/plugins/org.eclipse.jst.server.tomcat.ui/tomcatui/org/eclipse/jst/server/tomcat/ui/internal/editor/ConfigurationWebModuleEditorPart.java
index 74970ea..1b6f550 100644
--- a/plugins/org.eclipse.jst.server.tomcat.ui/tomcatui/org/eclipse/jst/server/tomcat/ui/internal/editor/ConfigurationWebModuleEditorPart.java
+++ b/plugins/org.eclipse.jst.server.tomcat.ui/tomcatui/org/eclipse/jst/server/tomcat/ui/internal/editor/ConfigurationWebModuleEditorPart.java
@@ -16,6 +16,7 @@
 import java.util.Iterator;
 import java.util.List;
 
+import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jface.dialogs.IDialogConstants;
 import org.eclipse.jface.viewers.ColumnWeightData;
@@ -371,10 +372,20 @@
 		if (s == null || s.length() < 2)
 			return true;
 		
+		// check absolute path
 		File f = new File(s);
 		if (f.exists())
 			return true;
 		
+		// check workspace
+		try {
+			if (ResourcesPlugin.getWorkspace().getRoot().getProject(s).exists())
+				return true;
+		} catch (Exception e) {
+			// bad path
+		}
+		
+		// check server relative path
 		try {
 			f = server.getRuntime().getLocation().append(s).toFile();
 			if (f.exists())
diff --git a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/ProjectModuleFactoryDelegate.java b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/ProjectModuleFactoryDelegate.java
index f2be864..ad9e256 100644
--- a/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/ProjectModuleFactoryDelegate.java
+++ b/plugins/org.eclipse.wst.server.core/servercore/org/eclipse/wst/server/core/util/ProjectModuleFactoryDelegate.java
@@ -372,6 +372,12 @@
 					if (".wtpmodules".equals(delta2.getResource().getName())) {
 						t.b = true;
 						return false;
+					} else if (".facets".equals(delta2.getResource().getName())) {
+						t.b = true;
+						return false;
+					} else if (".component".equals(delta2.getResource().getName())) {
+						t.b = true;
+						return false;
 					}
 					return true;
 				}
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Messages.properties b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Messages.properties
index 8434b7f..30e0139 100644
--- a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Messages.properties
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/Messages.properties
@@ -242,7 +242,7 @@
 runtimeTargetNone=<None>
 runtimeTargetNewRuntime=&New...
 runtimeTargetRuntimePreferences=Configure &Installed Runtimes...
-runtimeTargetChildren=Include child projects
+runtimeTargetChildren=Target contained projects
 
 # General dialogs
 defaultDialogTitle=Server
diff --git a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/RuntimeTargetComposite.java b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/RuntimeTargetComposite.java
index 953dad7..52714b8 100644
--- a/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/RuntimeTargetComposite.java
+++ b/plugins/org.eclipse.wst.server.ui/serverui/org/eclipse/wst/server/ui/internal/RuntimeTargetComposite.java
@@ -163,6 +163,7 @@
 			includeChildren.setText(Messages.runtimeTargetChildren);
 			data = new GridData();
 			data.horizontalSpan = 2;
+			data.horizontalIndent = 15;
 			includeChildren.setLayoutData(data);
 			includeChildren.setSelection(true);