[423759] Initial implementation for the "Serve modules without
publishing" feature for Tomcat 8
diff --git a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/Tomcat80Handler.java b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/Tomcat80Handler.java
index a72adb9..b3d0e3f 100644
--- a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/Tomcat80Handler.java
+++ b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/Tomcat80Handler.java
@@ -145,27 +145,8 @@
 	 * @see ITomcatVersionHandler#prepareForServingDirectly(IPath, TomcatServer)
 	 */
 	public IStatus prepareForServingDirectly(IPath baseDir, TomcatServer server, String tomcatVersion) {
-		IStatus status;
-		// If serving modules without publishing, loader jar is needed
-		// TODO Need to examine catalina.properties to ensure loader jar and catalina.properties are handled appropriately
-		if (server.isServeModulesWithoutPublish()) {
-			status = TomcatVersionHelper.copyLoaderJar(
-					getRuntimeBaseDirectory(server).append("lib"),
-					server.getServer().getRuntime().getRuntimeType().getId(), tomcatVersion);
-			// If copy successful and running a separate server instance, modify catalina.properties
-			if (status.isOK() && server.isTestEnvironment()) {
-				status = TomcatVersionHelper.updatePropertiesToServeDirectly(baseDir, "lib", "common");
-			}
-		}
-		// Else ensure jar is removed
-		else {
-			TomcatVersionHelper.removeLoaderJar(
-					getRuntimeBaseDirectory(server).append("lib"),
-					server.getServer().getRuntime().getRuntimeType().getId(), tomcatVersion);
-			// TODO Decide what to do with removal warning, maybe nothing
-			status = Status.OK_STATUS;
-		}
-		return status;
+		// Nothing beyond configuration required for Tomcat 8
+		return Status.OK_STATUS;
 	}
 
 	/**
@@ -176,13 +157,12 @@
 	}
 	
 	/**
-	 * Returns true since Tomcat 6.x supports this feature.
+	 * Returns true since Tomcat 8.x supports this feature.
 	 * 
 	 * @return true since feature is supported
 	 */
 	public boolean supportsServeModulesWithoutPublish() {
-		// TODO Provide new implementation for Tomcat 8.0.  For now, don't allow.
-		return false;
+		return true;
 	}
 
 	/**
diff --git a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/Tomcat80PublishModuleVisitor.java b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/Tomcat80PublishModuleVisitor.java
new file mode 100644
index 0000000..cdffa35
--- /dev/null
+++ b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/Tomcat80PublishModuleVisitor.java
@@ -0,0 +1,265 @@
+/**********************************************************************
+ * Copyright (c) 2014 SAS Institute and others.
+ * 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:
+ *    SAS Institute - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.jst.server.tomcat.core.internal;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jst.server.tomcat.core.internal.xml.server40.Context;
+import org.eclipse.jst.server.tomcat.core.internal.xml.server40.JarResources;
+import org.eclipse.jst.server.tomcat.core.internal.xml.server40.PostResources;
+import org.eclipse.jst.server.tomcat.core.internal.xml.server40.PreResources;
+import org.eclipse.jst.server.tomcat.core.internal.xml.server40.ServerInstance;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
+import org.eclipse.wst.common.componentcore.resources.IVirtualResource;
+import org.eclipse.wst.server.core.IModule;
+import org.eclipse.wst.server.core.ServerUtil;
+
+public class Tomcat80PublishModuleVisitor extends TomcatPublishModuleVisitor {
+
+	/**
+	 * Instantiate a new Tomcat80PublishModuleVisitor
+	 * 
+	 * @param baseDir catalina base path
+	 * @param tomcatVersion tomcat version
+	 * @param serverInstance ServerInstance containing server.xml contents
+	 * @param sharedLoader string value for shared.loader catalina configuration property
+	 * @param enableMetaInfResources flag to indicate if Servlet 3.0 "META-INF/resources" feature should be supported
+	 */
+	Tomcat80PublishModuleVisitor(IPath baseDir, String tomcatVersion, ServerInstance serverInstance, String sharedLoader, boolean enableMetaInfResources) {
+		super(baseDir, tomcatVersion, serverInstance, sharedLoader, enableMetaInfResources);
+	}
+
+    /**
+     * {@inheritDoc}
+     */
+	@Override
+    public void endVisitWebComponent(IVirtualComponent component)
+            throws CoreException {
+
+        // track context changes, don't rewrite if not needed
+        boolean dirty = false;
+
+        IModule module = ServerUtil.getModule(component.getProject());
+
+        // we need this for the user-specified context path
+        Context context = findContext(module);
+        if (context == null) {
+        	String name = module != null ? module.getName() : component.getName();
+    		Trace.trace(Trace.SEVERE, "Could not find context for module " + name);
+    		throw new CoreException(new Status(IStatus.ERROR, TomcatPlugin.PLUGIN_ID, 0,
+    				NLS.bind(Messages.errorPublishContextNotFound, name), null));
+        }
+
+		dirty = includeProjectContextXml(component, context);
+		dirty = updateDocBaseAndPath(component, context);
+
+		// Add WEB-INF/classes elements as PreResources
+		for (Iterator iterator = virtualClassClasspathElements.iterator();
+				iterator.hasNext();) {
+			Object virtualClassClasspathElement = iterator.next();
+			PreResources preResources = (PreResources)context.getResources().createElement("PreResources");
+			preResources.setClassName("org.apache.catalina.webresources.DirResourceSet");
+			preResources.setBase(virtualClassClasspathElement.toString());
+			preResources.setWebAppMount("/WEB-INF/classes");
+			preResources.setInternalPath("/");
+			preResources.setClassLoaderOnly("false");
+		}
+		virtualClassClasspathElements.clear();
+
+		// Add Jars as JarResources if a jar, or as PostResources if a utility project
+		for (Iterator iterator = virtualJarClasspathElements.iterator();
+				iterator.hasNext();) {
+			Object virtualJarClassClasspathElement = iterator.next();
+			String jarPath = virtualJarClassClasspathElement.toString();
+			if (jarPath.endsWith(".jar")) {
+				JarResources jarResources = (JarResources)context.getResources().createElement("JarResources");
+				jarResources.setClassName("org.apache.catalina.webresources.JarResourceSet");
+				jarResources.setBase(jarPath);
+				jarResources.setWebAppMount("/WEB-INF/classes");
+				jarResources.setInternalPath("/");
+				jarResources.setClassLoaderOnly("true");
+			}
+			else {
+				PostResources postResources = (PostResources)context.getResources().createElement("PostResources");
+				postResources.setClassName("org.apache.catalina.webresources.DirResourceSet");
+				postResources.setBase(jarPath);
+				postResources.setWebAppMount("/WEB-INF/classes");
+				postResources.setInternalPath("/");
+				postResources.setClassLoaderOnly("false");
+				// Map META-INF tld files to WEB-INF
+				File metaInfDir = new File(jarPath + "/META-INF");
+				if (metaInfDir.isDirectory() && metaInfDir.exists()) {
+					// Map META-INF directory directly to /META-INF
+					postResources = (PostResources)context.getResources().createElement("PostResources");
+					postResources.setClassName("org.apache.catalina.webresources.DirResourceSet");
+					postResources.setBase(metaInfDir.getPath());
+					postResources.setWebAppMount("/META-INF");
+					postResources.setInternalPath("/");
+					postResources.setClassLoaderOnly("false");
+
+					File [] tldFiles = metaInfDir.listFiles(new FileFilter() {
+							public boolean accept(File file) {
+								if (file.isFile() && file.getName().endsWith(".tld")) {
+									return true;
+								}
+								return false;
+							}
+						});
+					for (int i = 0; i < tldFiles.length; i++) {
+						postResources = (PostResources)context.getResources().createElement("PostResources");
+						postResources.setClassName("org.apache.catalina.webresources.FileResourceSet");
+						postResources.setBase(tldFiles[0].getPath());
+						postResources.setWebAppMount("/WEB-INF/" + tldFiles[0].getName());
+						postResources.setInternalPath("/");
+						postResources.setClassLoaderOnly("false");
+					}
+				}
+			}
+		}
+		virtualJarClasspathElements.clear();
+
+		Set<String> rtPathsProcessed = new HashSet<String>();
+		Set<String> locationsIncluded = new HashSet<String>();
+		String docBase = context.getDocBase();
+		locationsIncluded.add(docBase);
+		Map<String, String> retryLocations = new HashMap<String, String>();
+		IVirtualResource [] virtualResources = component.getRootFolder().getResources("");
+		// Loop over the module's resources
+		for (int i = 0; i < virtualResources.length; i++) {
+			String rtPath = virtualResources[i].getRuntimePath().toString();
+			// Note: The virtual resources returned only know their runtime path.
+			// Asking for the project path for this resource performs a lookup
+			// that will only return the path for the first mapping for the
+			// runtime path.  Thus use of getUnderlyingResources() is necessary.
+			// However, this returns matching resources from all mappings so
+			// we have to try to keep only those that are mapped directly
+			// to the runtime path in the .components file.
+
+			// If this runtime path has not yet been processed
+			if (!rtPathsProcessed.contains(rtPath)) {
+				// If not a Java related resource
+				if (!"/WEB-INF/classes".equals(rtPath)) {
+					// Get all resources for this runtime path
+					IResource[] underlyingResources = virtualResources[i].getUnderlyingResources();
+					// If resource is mapped to "/", then we know it corresponds directly
+					// to a mapping in the .components file
+					if ("/".equals(rtPath)) {
+						for (int j = 0; j < underlyingResources.length; j++) {
+							IPath resLoc = underlyingResources[j].getLocation();
+							String location = resLoc.toOSString();
+							if (!location.equals(docBase)) {
+								PreResources preResources = (PreResources)context.getResources().createElement("PreResources");
+								preResources.setClassName("org.apache.catalina.webresources.DirResourceSet");
+								preResources.setBase(location);
+								preResources.setWebAppMount("/");
+								preResources.setInternalPath("/");
+								preResources.setClassLoaderOnly("false");
+								// Add to the set of locations included
+								locationsIncluded.add(location);
+							}
+						}
+					}
+					// Else this runtime path is something other than "/"
+					else {
+						int idx = rtPath.lastIndexOf('/');
+						// If a "normal" runtime path
+						if (idx >= 0) {
+							// Get the name of the last segment in the runtime path
+							String lastSegment = rtPath.substring(idx + 1);
+							// Check the underlying resources to determine which correspond to mappings
+							for (int j = 0; j < underlyingResources.length; j++) {
+								IPath resLoc = underlyingResources[j].getLocation();
+								String location = resLoc.toOSString();
+								// If the last segment of the runtime path doesn't match the
+								// the last segment of the location, then we have a direct mapping
+								// from the .contents file.
+								if (!lastSegment.equals(resLoc.lastSegment())) {
+									PreResources preResources = (PreResources)context.getResources().createElement("PreResources");
+									preResources.setClassName("org.apache.catalina.webresources.DirResourceSet");
+									preResources.setBase(location);
+									preResources.setWebAppMount(rtPath);
+									preResources.setInternalPath("/");
+									preResources.setClassLoaderOnly("false");
+									// Add to the set of locations included
+									locationsIncluded.add(location);
+								}
+								// Else last segment of runtime path did match the last segment
+								// of the location.  We likely have a subfolder of a mapping
+								// that matches a portion of the runtime path.
+								else {
+									// Since we can't be sure, save so it can be check again later
+									retryLocations.put(location, rtPath);
+								}
+							}
+						}
+					}
+				}
+				// Add the runtime path to those already processed
+				rtPathsProcessed.add(rtPath);
+			}
+		}
+		// If there are locations to retry, add any not yet included in extra paths setting
+		if (!retryLocations.isEmpty()) {
+			// Remove retry locations already included in the extra paths
+			for (Iterator iterator = retryLocations.keySet().iterator(); iterator.hasNext();) {
+				String location = (String)iterator.next();
+				for (Iterator iterator2 = locationsIncluded.iterator(); iterator2.hasNext();) {
+					String includedLocation = (String)iterator2.next();
+					if (location.equals(includedLocation) || location.startsWith(includedLocation + File.separator)) {
+						iterator.remove();
+						break;
+					}
+				}
+			}
+			// If any entries are left, include them in the extra paths
+			if (!retryLocations.isEmpty()) {
+				for (Iterator iterator = retryLocations.entrySet().iterator(); iterator.hasNext();) {
+					Map.Entry entry = (Map.Entry)iterator.next();
+					String location = (String)entry.getKey();
+					String rtPath = (String)entry.getValue();
+					PreResources preResources = (PreResources)context.getResources().createElement("PreResources");
+					preResources.setClassName("org.apache.catalina.webresources.DirResourceSet");
+					preResources.setBase(location);
+					preResources.setWebAppMount(rtPath);
+					preResources.setInternalPath("/");
+					preResources.setClassLoaderOnly("false");
+				}
+			}
+		}
+		if (!virtualDependentResources.isEmpty()) {
+			// TODO Implement support for WebFragment resources
+//			for (Map.Entry<String, List<String>> entry : virtualDependentResources.entrySet()) {
+//				String rtPath = entry.getKey();
+//				List<String> locations = entry.getValue();
+//				for (String location : locations) {
+//
+//				}
+//			}
+		}
+		virtualDependentResources.clear();
+
+		if (dirty) {
+			//TODO If writing to separate context XML files, save "dirty" status for later use
+		}
+	}
+}
diff --git a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatPublishModuleVisitor.java b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatPublishModuleVisitor.java
index 45adcac..ed2608a 100644
--- a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatPublishModuleVisitor.java
+++ b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatPublishModuleVisitor.java
@@ -200,68 +200,8 @@
     				NLS.bind(Messages.errorPublishContextNotFound, name), null));
         }
 
-        String contextName = null;
-        boolean reloadable = true;
-
-        contextName = context.getPath();
-        reloadable = Boolean.valueOf(context.getReloadable()).booleanValue();
-
-        // now strip initial /
-        if (contextName.startsWith("/")) {
-            contextName = contextName.substring(1);
-        }
-
-        // root context is deployed with the "ROOT" name in tomcat
-        if ("".equals(contextName)) {
-            contextName = "ROOT";
-        }
-
-        // handle project context.xml
-        Context projectContext = getProjectContextXml(component);
-
-        if (projectContext != null) {
-            // copy configuration to server context
-            projectContext.copyChildrenTo(context);
-
-            Map attrs = projectContext.getAttributes();
-            Iterator iter = attrs.keySet().iterator();
-            while (iter.hasNext()) {
-                String name = (String) iter.next();
-                if (!name.equalsIgnoreCase("path")
-                        && !name.equalsIgnoreCase("docBase")
-                        && !name.equalsIgnoreCase("source")) {
-                    String value = (String) attrs.get(name);
-                    String actualValue = context.getAttributeValue(name);
-                    if (!value.equals(actualValue)) {
-                        context.setAttributeValue(name, value);
-                        dirty = true;
-                    }
-                }
-            }
-        }
-
-        // handle changes in docBase
-        String docBase = component.getRootFolder().getUnderlyingFolder()
-                .getLocation().toOSString();
-        if (!docBase.equals(context.getDocBase())) {
-            dirty = true;
-            context.setDocBase(docBase);
-        }
-
-        // handle changes in reloadable flag
-        if (reloadable != (Boolean.valueOf((context.getReloadable()))
-                .booleanValue())) {
-            dirty = true;
-            context.setReloadable(Boolean.toString(reloadable));
-        }
-
-        String path = (contextName.equals("ROOT") ? "" : "/" + contextName);
-        // handle changes in the path
-        // PATH is required for tomcat 5.0, but ignored in tomcat 5.5
-        if (!path.equals(context.getPath())) {
-            dirty = true;
-            context.setPath(path);
-        }
+		dirty = includeProjectContextXml(component, context);
+		dirty = updateDocBaseAndPath(component, context);
 
         context.getResources().setClassName(
                 "org.eclipse.jst.server.tomcat.loader.WtpDirContext");
@@ -309,6 +249,7 @@
 
 		Set<String> rtPathsProcessed = new HashSet<String>();
 		Set<String> locationsIncluded = new HashSet<String>();
+		String docBase = context.getDocBase();
 		locationsIncluded.add(docBase);
 		Map<String, String> retryLocations = new HashMap<String, String>();
 		IVirtualResource [] virtualResources = component.getRootFolder().getResources("");
@@ -527,6 +468,7 @@
 				}
 			}			
 		}
+		virtualDependentResources.clear();
 
 		// Combine the classes and jar virtual classpaths
 		if (vcJarBuffer.length() > 0) {
@@ -637,4 +579,67 @@
 		}
         return null;
     }
+
+	String getContextName(Context context) {
+		String contextName = context.getPath();
+
+		// now strip initial /
+		if (contextName.startsWith("/")) {
+			contextName = contextName.substring(1);
+		}
+
+		// root context is deployed with the "ROOT" name in tomcat
+		if ("".equals(contextName)) {
+			contextName = "ROOT";
+		}
+		return contextName;
+	}
+
+	boolean includeProjectContextXml(IVirtualComponent component, Context context) throws CoreException {
+		boolean dirty = false;
+		// handle project context.xml
+		Context projectContext = getProjectContextXml(component);
+
+		if (projectContext != null) {
+			// copy configuration to server context
+			projectContext.copyChildrenTo(context);
+
+			Map attrs = projectContext.getAttributes();
+			Iterator iter = attrs.keySet().iterator();
+			while (iter.hasNext()) {
+				String name = (String) iter.next();
+				if (!name.equalsIgnoreCase("path")
+						&& !name.equalsIgnoreCase("docBase")
+						&& !name.equalsIgnoreCase("source")) {
+					String value = (String) attrs.get(name);
+					String actualValue = context.getAttributeValue(name);
+					if (!value.equals(actualValue)) {
+						context.setAttributeValue(name, value);
+						dirty = true;
+					}
+				}
+			}
+		}
+		return dirty;
+	}
+
+	boolean updateDocBaseAndPath(IVirtualComponent component, Context context) {
+		boolean dirty = false;
+		// handle changes in docBase
+		String docBase = component.getRootFolder().getUnderlyingFolder().getLocation().toOSString();
+		if (!docBase.equals(context.getDocBase())) {
+			dirty = true;
+			context.setDocBase(docBase);
+		}
+
+		String contextName = getContextName(context);
+		String path = (contextName.equals("ROOT") ? "" : "/" + contextName);
+		// handle changes in the path
+		// PATH is required for tomcat 5.0, but ignored in tomcat 5.5
+		if (!path.equals(context.getPath())) {
+			dirty = true;
+			context.setPath(path);
+		}
+		return dirty;
+	}
 }
diff --git a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatVersionHelper.java b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatVersionHelper.java
index bcf66e6..00c396b 100644
--- a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatVersionHelper.java
+++ b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/TomcatVersionHelper.java
@@ -911,9 +911,17 @@
 
 			boolean modified = false;
 
+			boolean isTomcat8 = tomcatVersion.startsWith("8.");
 			// care about top-level modules only
-			TomcatPublishModuleVisitor visitor = new TomcatPublishModuleVisitor(
-					baseDir, tomcatVersion, publishedInstance, loader, enableMetaInfResources);
+			TomcatPublishModuleVisitor visitor;
+			if (isTomcat8) {
+				visitor = new Tomcat80PublishModuleVisitor(
+						baseDir, tomcatVersion, publishedInstance, loader, enableMetaInfResources);
+			}
+			else {
+				visitor = new TomcatPublishModuleVisitor(
+						baseDir, tomcatVersion, publishedInstance, loader, enableMetaInfResources);
+			}
 			Context [] contexts = publishedInstance.getContexts();
 			for (int i = 0; i < contexts.length; i++) {
 				String moduleId = contexts[i].getSource();
@@ -1166,9 +1174,10 @@
 			// If a version string needs to be acquired
 			if (versionSubString == null) {
 				InputStream is = null;
+				JarFile jar = null;
 				try {
 					// Read version string from catalina.jar
-					JarFile jar = new JarFile(jarFile);
+					jar = new JarFile(jarFile);
 					JarEntry entry = jar.getJarEntry("org/apache/catalina/util/ServerInfo.properties");
 					if (entry != null) {
 						is = jar.getInputStream(entry);
@@ -1198,6 +1207,13 @@
 							// Ignore
 						}
 					}
+					if (jar != null) {
+						try {
+							jar.close();
+						} catch (IOException e) {
+							// Ignore
+						}
+					}
 				}
 			}
 			if (versionSubString != null) {
diff --git a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/xml/server40/JarResources.java b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/xml/server40/JarResources.java
new file mode 100644
index 0000000..a44896b
--- /dev/null
+++ b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/xml/server40/JarResources.java
@@ -0,0 +1,96 @@
+/**********************************************************************
+ * Copyright (c) 2014 SAS Institute and others.
+ * 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:
+ *    SAS Institute - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.jst.server.tomcat.core.internal.xml.server40;
+
+import org.eclipse.jst.server.tomcat.core.internal.xml.XMLElement;
+
+public class JarResources extends XMLElement {
+
+	/**
+	 * Get className attribute
+	 * @return className attribute value
+	 */
+	public String getClassName() {
+		return getAttributeValue("className");
+	}
+
+	/**
+	 * Set base attribute.
+	 * @param base value to set
+	 */
+	public void setClassName(String className) {
+		setAttributeValue("className", className);
+	}
+
+	/**
+	 * Get base attribute
+	 * @return base attribute value
+	 */
+	public String getBase() {
+		return getAttributeValue("base");
+	}
+
+	/**
+	 * Set base attribute.
+	 * @param base value to set
+	 */
+	public void setBase(String base) {
+		setAttributeValue("base", base);
+	}
+
+	/**
+	 * Get webAppMount attribute
+	 * @return base attribute value
+	 */
+	public String getWebAppMount() {
+		return getAttributeValue("webAppMount");
+	}
+
+	/**
+	 * Set webAppMount attribute.
+	 * @param webAppMount value to set
+	 */
+	public void setWebAppMount(String webAppMount) {
+		setAttributeValue("webAppMount", webAppMount);
+	}
+
+	/**
+	 * Get internalPath attribute
+	 * @return internalPath attribute value
+	 */
+	public String getInternalPath() {
+		return getAttributeValue("internalPath");
+	}
+
+	/**
+	 * Set internalPath attribute.
+	 * @param internalPath value to set
+	 */
+	public void setInternalPath(String internalPath) {
+		setAttributeValue("internalPath", internalPath);
+	}
+
+	/**
+	 * Get classLoaderOnly attribute
+	 * @return classLoaderOnly attribute value
+	 */
+	public String getClassLoaderOnly() {
+		return getAttributeValue("classLoaderOnly");
+	}
+
+	/**
+	 * Set classLoaderOnly attribute.
+	 * @param classLoaderOnly value to set
+	 */
+	public void setClassLoaderOnly(String classLoaderOnly) {
+		setAttributeValue("classLoaderOnly", classLoaderOnly);
+	}
+}
diff --git a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/xml/server40/PostResources.java b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/xml/server40/PostResources.java
new file mode 100644
index 0000000..37b99f6
--- /dev/null
+++ b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/xml/server40/PostResources.java
@@ -0,0 +1,96 @@
+/**********************************************************************
+ * Copyright (c) 2014 SAS Institute and others.
+ * 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:
+ *    SAS Institute - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.jst.server.tomcat.core.internal.xml.server40;
+
+import org.eclipse.jst.server.tomcat.core.internal.xml.XMLElement;
+
+public class PostResources extends XMLElement {
+
+	/**
+	 * Get className attribute
+	 * @return className attribute value
+	 */
+	public String getClassName() {
+		return getAttributeValue("className");
+	}
+
+	/**
+	 * Set base attribute.
+	 * @param base value to set
+	 */
+	public void setClassName(String className) {
+		setAttributeValue("className", className);
+	}
+
+	/**
+	 * Get base attribute
+	 * @return base attribute value
+	 */
+	public String getBase() {
+		return getAttributeValue("base");
+	}
+
+	/**
+	 * Set base attribute.
+	 * @param base value to set
+	 */
+	public void setBase(String base) {
+		setAttributeValue("base", base);
+	}
+
+	/**
+	 * Get webAppMount attribute
+	 * @return base attribute value
+	 */
+	public String getWebAppMount() {
+		return getAttributeValue("webAppMount");
+	}
+
+	/**
+	 * Set webAppMount attribute.
+	 * @param webAppMount value to set
+	 */
+	public void setWebAppMount(String webAppMount) {
+		setAttributeValue("webAppMount", webAppMount);
+	}
+
+	/**
+	 * Get internalPath attribute
+	 * @return internalPath attribute value
+	 */
+	public String getInternalPath() {
+		return getAttributeValue("internalPath");
+	}
+
+	/**
+	 * Set internalPath attribute.
+	 * @param internalPath value to set
+	 */
+	public void setInternalPath(String internalPath) {
+		setAttributeValue("internalPath", internalPath);
+	}
+
+	/**
+	 * Get classLoaderOnly attribute
+	 * @return classLoaderOnly attribute value
+	 */
+	public String getClassLoaderOnly() {
+		return getAttributeValue("classLoaderOnly");
+	}
+
+	/**
+	 * Set classLoaderOnly attribute.
+	 * @param classLoaderOnly value to set
+	 */
+	public void setClassLoaderOnly(String classLoaderOnly) {
+		setAttributeValue("classLoaderOnly", classLoaderOnly);
+	}
+}
diff --git a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/xml/server40/PreResources.java b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/xml/server40/PreResources.java
new file mode 100644
index 0000000..3ef579f
--- /dev/null
+++ b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/xml/server40/PreResources.java
@@ -0,0 +1,96 @@
+/**********************************************************************
+ * Copyright (c) 2014 SAS Institute and others.
+ * 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:
+ *    SAS Institute - Initial API and implementation
+ **********************************************************************/
+package org.eclipse.jst.server.tomcat.core.internal.xml.server40;
+
+import org.eclipse.jst.server.tomcat.core.internal.xml.XMLElement;
+
+public class PreResources extends XMLElement {
+
+	/**
+	 * Get className attribute
+	 * @return className attribute value
+	 */
+	public String getClassName() {
+		return getAttributeValue("className");
+	}
+
+	/**
+	 * Set base attribute.
+	 * @param base value to set
+	 */
+	public void setClassName(String className) {
+		setAttributeValue("className", className);
+	}
+
+	/**
+	 * Get base attribute
+	 * @return base attribute value
+	 */
+	public String getBase() {
+		return getAttributeValue("base");
+	}
+
+	/**
+	 * Set base attribute.
+	 * @param base value to set
+	 */
+	public void setBase(String base) {
+		setAttributeValue("base", base);
+	}
+
+	/**
+	 * Get webAppMount attribute
+	 * @return base attribute value
+	 */
+	public String getWebAppMount() {
+		return getAttributeValue("webAppMount");
+	}
+
+	/**
+	 * Set webAppMount attribute.
+	 * @param webAppMount value to set
+	 */
+	public void setWebAppMount(String webAppMount) {
+		setAttributeValue("webAppMount", webAppMount);
+	}
+
+	/**
+	 * Get internalPath attribute
+	 * @return internalPath attribute value
+	 */
+	public String getInternalPath() {
+		return getAttributeValue("internalPath");
+	}
+
+	/**
+	 * Set internalPath attribute.
+	 * @param internalPath value to set
+	 */
+	public void setInternalPath(String internalPath) {
+		setAttributeValue("internalPath", internalPath);
+	}
+
+	/**
+	 * Get classLoaderOnly attribute
+	 * @return classLoaderOnly attribute value
+	 */
+	public String getClassLoaderOnly() {
+		return getAttributeValue("classLoaderOnly");
+	}
+
+	/**
+	 * Set classLoaderOnly attribute.
+	 * @param classLoaderOnly value to set
+	 */
+	public void setClassLoaderOnly(String classLoaderOnly) {
+		setAttributeValue("classLoaderOnly", classLoaderOnly);
+	}
+}