[Bug 188163] Add support for classpath dependencies and fix handling of jars added via J2EE Module Dependencies
diff --git a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/wst/ModuleTraverser.java b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/wst/ModuleTraverser.java
index 472ef11..5a1cc16 100644
--- a/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/wst/ModuleTraverser.java
+++ b/plugins/org.eclipse.jst.server.tomcat.core/tomcatcore/org/eclipse/jst/server/tomcat/core/internal/wst/ModuleTraverser.java
@@ -10,18 +10,25 @@
**********************************************************************/
package org.eclipse.jst.server.tomcat.core.internal.wst;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.URI;
+import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jst.server.tomcat.core.internal.TomcatPlugin;
@@ -34,7 +41,6 @@
import org.eclipse.wst.common.componentcore.internal.WorkbenchComponent;
import org.eclipse.wst.common.componentcore.internal.impl.ModuleURIUtil;
import org.eclipse.wst.common.componentcore.internal.impl.PlatformURLModuleConnection;
-import org.eclipse.wst.common.componentcore.internal.resources.VirtualArchiveComponent;
import org.eclipse.wst.common.componentcore.internal.util.IModuleConstants;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
import org.eclipse.wst.server.core.IModule;
@@ -60,6 +66,19 @@
public static final String UTILITY_MODULE = IModuleConstants.JST_UTILITY_MODULE;
/**
+ * Name of the custom Java classpath entry attribute that is used to flag entries
+ * which should be exposed as module dependencies via the virtual component API.
+ */
+ public static final String CLASSPATH_COMPONENT_DEPENDENCY = "org.eclipse.jst.component.dependency"; //$NON-NLS-1
+
+ /**
+ * Name of the custom Java classpath entry attribute that is used to flag
+ * the resolved entries of classpath containers that should not be exposed
+ * via the virtual component API.
+ */
+ public static final String CLASSPATH_COMPONENT_NON_DEPENDENCY = "org.eclipse.jst.component.nondependency"; //$NON-NLS-1
+
+ /**
* Scans the module using the specified visitor.
*
* @param module module to traverse
@@ -149,11 +168,18 @@
if (PlatformURLModuleConnection.CLASSPATH.equals(
refHandle.segment(ModuleURIUtil.ModuleURI.SUB_PROTOCOL_INDX))) {
IJavaProject jproj = JavaCore.create(proj);
- String classpathKind = refHandle.segment(1);
- IPath classpathRef = getSuffixPath(refHandle, 2);
-
- visitor.visitClasspathEntry(rtFolder, getClasspathEntry(
- jproj, classpathKind, classpathRef));
+ IPath refPath = getResolvedPathForArchiveComponent(refHandle);
+ // If an archive component, add to list
+ if (refPath != null) {
+ if (!refPath.isAbsolute()) {
+ IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(refPath);
+ refPath = file.getLocation();
+ }
+ visitor.visitArchiveComponent(rtFolder, refPath);
+ }
+ else {
+ // TODO Determine if any use case would arrive here.
+ }
} else {
try {
WorkbenchComponent childCom = warStruct.findComponentByURI(refHandle);
@@ -175,17 +201,6 @@
visitor.endVisitWebComponent(component);
}
- private static IPath getSuffixPath(URI uri, int index) {
- StringBuffer result = new StringBuffer();
- String[] segments = uri.segments();
- for (int i = index; i < segments.length; i++) {
- if (i > index)
- result.append('/');
- result.append(segments[i]);
- }
- return new Path(result.toString());
- }
-
private static void traverseWebComponentLocalEntries(
WorkbenchComponent comp, IModuleVisitor visitor,
IProgressMonitor monitor) throws CoreException {
@@ -204,6 +219,19 @@
visitor.visitWebResource(childComp.getRuntimePath(), getOSPath(
warProject, project, cpe.getOutputLocation()));
}
+
+ // Include tagged classpath entries
+ Map classpathDeps = getComponentClasspathDependencies(project, true);
+ for (Iterator iterator = classpathDeps.keySet().iterator(); iterator.hasNext();) {
+ IClasspathEntry entry = (IClasspathEntry)iterator.next();
+ IClasspathAttribute attrib = (IClasspathAttribute)classpathDeps.get(entry);
+ String rtFolder = attrib.getValue();
+ if (rtFolder == null) {
+ rtFolder = "/WEB-INF/lib";
+ }
+ // TODO Determine if different handling is needed for some use cases
+ visitor.visitArchiveComponent(new Path(rtFolder), entry.getPath());
+ }
}
private static void traverseDependentEntries(IModuleVisitor visitor,
@@ -228,6 +256,19 @@
.append(name + ".jar"), getOSPath(dependentProject,
project, cpe.getOutputLocation()));
}
+
+ // Include tagged classpath entries
+ Map classpathDeps = getComponentClasspathDependencies(project, false);
+ for (Iterator iterator = classpathDeps.keySet().iterator(); iterator.hasNext();) {
+ IClasspathEntry entry = (IClasspathEntry)iterator.next();
+ IClasspathAttribute attrib = (IClasspathAttribute)classpathDeps.get(entry);
+ String rtFolder = attrib.getValue();
+ if (rtFolder == null) {
+ rtFolder = "/WEB-INF/lib";
+ }
+ // TODO Determine if different handling is needed for some use cases
+ visitor.visitArchiveComponent(new Path(rtFolder), entry.getPath());
+ }
}
private static IClasspathEntry getClasspathEntry(IJavaProject project,
@@ -241,26 +282,6 @@
return null;
}
- private static IClasspathEntry getClasspathEntry(IJavaProject project,
- String classpathKind, IPath classpathRef) throws JavaModelException {
- int entryKind;
- if (VirtualArchiveComponent.LIBARCHIVETYPE.equals(classpathKind)) {
- entryKind = IClasspathEntry.CPE_LIBRARY;
- } else if (VirtualArchiveComponent.VARARCHIVETYPE.equals(classpathKind)) {
- entryKind = IClasspathEntry.CPE_VARIABLE;
- } else {
- return null;
- }
- IClasspathEntry[] cp = project.getRawClasspath();
- for (int i = 0; i < cp.length; i++) {
- if (entryKind == cp[i].getEntryKind()
- && classpathRef.equals(cp[i].getPath())) {
- return JavaCore.getResolvedClasspathEntry(cp[i]);
- }
- }
- return null;
- }
-
private static IPath getOSPath(IProject project, IJavaProject javaProject,
IPath outputPath) throws JavaModelException {
if (outputPath == null)
@@ -269,4 +290,202 @@
.getLocation();
}
+ /*
+ * Derived from J2EEProjectUtilities.getResolvedPathForArchiveComponent()
+ */
+ private static IPath getResolvedPathForArchiveComponent(URI uri) {
+
+ String resourceType = uri.segment(1);
+ URI contenturi = ModuleURIUtil.trimToRelativePath(uri, 2);
+ String contentName = contenturi.toString();
+
+ if (resourceType.equals("lib")) { //$NON-NLS-1$
+ // module:/classpath/lib/D:/foo/foo.jar
+ return Path.fromOSString(contentName);
+
+ } else if (resourceType.equals("var")) { //$NON-NLS-1$
+
+ // module:/classpath/var/<CLASSPATHVAR>/foo.jar
+ String classpathVar = contenturi.segment(0);
+ URI remainingPathuri = ModuleURIUtil.trimToRelativePath(contenturi, 1);
+ String remainingPath = remainingPathuri.toString();
+
+ String[] classpathvars = JavaCore.getClasspathVariableNames();
+ boolean found = false;
+ for (int i = 0; i < classpathvars.length; i++) {
+ if (classpathVar.equals(classpathvars[i])) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ IPath path = JavaCore.getClasspathVariable(classpathVar);
+ URI finaluri = URI.createURI(path.toOSString() + IPath.SEPARATOR + remainingPath);
+ return Path.fromOSString(finaluri.toString());
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Derived from ClasspathDependencyUtil.getComponentClasspathDependencies()
+ */
+ private static Map getComponentClasspathDependencies(final IJavaProject javaProject, final boolean isWebApp) throws CoreException {
+
+ // get the raw entries
+ final Map referencedRawEntries = getRawComponentClasspathDependencies(javaProject);
+ final Map validRawEntries = new HashMap();
+
+ // filter out non-valid referenced raw entries
+ final Iterator i = referencedRawEntries.keySet().iterator();
+ while (i.hasNext()) {
+ final IClasspathEntry entry = (IClasspathEntry) i.next();
+ final IClasspathAttribute attrib = (IClasspathAttribute) referencedRawEntries.get(entry);
+ if (isValid(entry, attrib, isWebApp, javaProject.getProject())) {
+ validRawEntries.put(entry, attrib);
+ }
+ }
+
+ // if we have no valid raw entries, return empty map
+ if (validRawEntries.isEmpty()) {
+ return Collections.EMPTY_MAP;
+ }
+
+ // XXX Would like to replace the code below with use of a public JDT API that returns
+ // the raw IClasspathEntry for a given resolved IClasspathEntry (see see https://bugs.eclipse.org/bugs/show_bug.cgi?id=183995)
+ // The code must currently leverage IPackageFragmentRoot to determine this
+ // mapping and, because IPackageFragmentRoots do not maintain IClasspathEntry data, a prior
+ // call is needed to getResolvedClasspath() and the resolved IClasspathEntries have to be stored in a Map from IPath-to-IClasspathEntry to
+ // support retrieval using the resolved IPackageFragmentRoot
+
+ // retrieve the resolved classpath
+ final IClasspathEntry[] entries = javaProject.getResolvedClasspath(true);
+ final Map pathToResolvedEntry = new HashMap();
+
+ // store in a map from path to entry
+ for (int j = 0; j < entries.length; j++) {
+ pathToResolvedEntry.put(entries[j].getPath(), entries[j]);
+ }
+
+ final Map referencedEntries = new HashMap();
+
+ // grab all IPackageFragmentRoots
+ final IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots();
+ for (int j = 0; j < roots.length; j++) {
+ final IPackageFragmentRoot root = roots[j];
+ final IClasspathEntry rawEntry = root.getRawClasspathEntry();
+
+ // is the raw entry valid?
+ IClasspathAttribute attrib = (IClasspathAttribute) validRawEntries.get(rawEntry);
+ if (attrib == null) {
+ continue;
+ }
+
+ final IPath pkgFragPath = root.getPath();
+ final IClasspathEntry resolvedEntry = (IClasspathEntry) pathToResolvedEntry.get(pkgFragPath);
+ final IClasspathAttribute resolvedAttrib = checkForComponentDependencyAttribute(resolvedEntry);
+ // attribute for the resolved entry must either be unspecified or it must be the
+ // dependency attribute for it to be included
+ if (resolvedAttrib == null || resolvedAttrib.getName().equals(CLASSPATH_COMPONENT_DEPENDENCY)) {
+ // filter out resolved entry if it doesn't pass the validation rules
+ if (isValid(resolvedEntry, resolvedAttrib != null ? resolvedAttrib : attrib, isWebApp, javaProject.getProject())) {
+ if (resolvedAttrib != null) {
+ // if there is an attribute on the sub-entry, use that
+ attrib = resolvedAttrib;
+ }
+ referencedEntries.put(resolvedEntry, attrib);
+ }
+ }
+ }
+
+ return referencedEntries;
+ }
+
+ /*
+ * Derived from ClasspathDependencyUtil.getRawComponentClasspathDependencies()
+ */
+ private static Map getRawComponentClasspathDependencies(final IJavaProject javaProject) throws CoreException {
+ if (javaProject == null) {
+ return Collections.EMPTY_MAP;
+ }
+ final Map referencedRawEntries = new HashMap();
+ final IClasspathEntry[] entries = javaProject.getRawClasspath();
+ for (int i = 0; i < entries.length; i++) {
+ final IClasspathEntry entry = entries[i];
+ final IClasspathAttribute attrib = checkForComponentDependencyAttribute(entry);
+ if (attrib != null) {
+ referencedRawEntries.put(entry, attrib);
+ }
+ }
+ return referencedRawEntries;
+ }
+
+ /*
+ * Derived from ClasspathDependencyUtil.checkForComponentDependencyAttribute()
+ */
+ private static IClasspathAttribute checkForComponentDependencyAttribute(final IClasspathEntry entry) {
+ if (entry == null) {
+ return null;
+ }
+ final IClasspathAttribute[] attributes = entry.getExtraAttributes();
+ for (int i = 0; i < attributes.length; i++) {
+ final IClasspathAttribute attribute = attributes[i];
+ final String name = attribute.getName();
+ if (name.equals(CLASSPATH_COMPONENT_DEPENDENCY)
+ || name.equals(CLASSPATH_COMPONENT_NON_DEPENDENCY)) {
+ return attribute;
+ }
+ }
+ return null;
+ }
+
+ /*
+ * Derived from ClasspathDependencyValidator.validateVirtualComponentEntry()
+ */
+ private static boolean isValid(final IClasspathEntry entry, final IClasspathAttribute attrib, boolean isWebApp, final IProject project) {
+ int kind = entry.getEntryKind();
+ if (kind == IClasspathEntry.CPE_LIBRARY) {
+ // does the path refer to a file or a folder?
+ final IPath entryPath = entry.getPath();
+ IPath entryLocation = entryPath;
+ final IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(entryPath);
+ if (resource != null) {
+ entryLocation = resource.getLocation();
+ }
+ if (entryLocation.toFile().isDirectory()) {
+ return false;
+ }
+ }
+ else if (kind == IClasspathEntry.CPE_PROJECT || kind == IClasspathEntry.CPE_SOURCE) {
+ return false;
+ }
+
+ String runtimePath = getRuntimePath(attrib, isWebApp);
+ if (!isWebApp) {
+ if (!entry.isExported() || !runtimePath.equals("../")) {
+ return false;
+ }
+ }
+ else {
+ if (runtimePath != null && !runtimePath.equals("/WEB-INF/lib")
+ && !runtimePath.equals("/WEB-INF/classes")
+ && !runtimePath.equals("../")) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /*
+ * Derived from ClasspathDependencyUtil.getRuntimePath()
+ */
+ private static String getRuntimePath(final IClasspathAttribute attrib, boolean isWebApp) {
+ if (attrib != null && !attrib.getName().equals(CLASSPATH_COMPONENT_DEPENDENCY)) {
+ return null;
+ }
+ if (attrib == null || attrib.getValue()== null || attrib.getValue().length() == 0) {
+ return isWebApp ? "/WEB-INF/lib" : "../";
+ }
+ return attrib.getValue();
+ }
}