[159888] TaglibHelper.addTEIVariables() opens same jar file about 70 times during project creation
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/taglib/BuildPathClassLoader.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/taglib/BuildPathClassLoader.java
new file mode 100644
index 0000000..db75b26
--- /dev/null
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/taglib/BuildPathClassLoader.java
@@ -0,0 +1,228 @@
+/*******************************************************************************
+ * Copyright (c) 2009 IBM Corporation 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:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jst.jsp.core.internal.taglib;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.eclipse.core.resources.IFile;
+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.Platform;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jst.jsp.core.internal.Logger;
+import org.eclipse.wst.sse.core.utils.StringUtils;
+
+/**
+ * Custom ClassLoader backed by a Java Project.
+ */
+public class BuildPathClassLoader extends ClassLoader {
+	private static final boolean DEBUG = Boolean.valueOf(Platform.getDebugOption("org.eclipse.jst.jsp.core/debug/taglibclassloader")).booleanValue(); //$NON-NLS-1$
+	private IJavaProject fProject;
+
+	public BuildPathClassLoader(ClassLoader parent, IJavaProject project) {
+		super(parent);
+		fProject = project;
+	}
+
+	/**
+	 * Closes the given file with "extreme prejudice".
+	 * 
+	 * @param file the zip file to be closed
+	 */
+	public void closeJarFile(ZipFile file) {
+		if (file == null)
+			return;
+		try {
+			file.close();
+		}
+		catch (IOException ioe) {
+			// no cleanup can be done
+			Logger.logException("JarUtilities: Could not close file " + file.getName(), ioe); //$NON-NLS-1$
+		}
+	}
+
+	/*
+	 * This may pose a runtime performance problem as it opens the containing
+	 * .jar file for each class, but this is countered by no longer leaving
+	 * file handles open nor having to directly interact the build path at
+	 * all. If it is a problem, the TaglibHelper should control some
+	 * "batching" whereby we leave the JarFiles open until directed to close
+	 * them at the end of TaglibHelper.addTEIVariables(...).
+	 * 
+	 * @see java.lang.ClassLoader#findClass(java.lang.String)
+	 */
+	protected Class findClass(String className) throws ClassNotFoundException {
+		if (DEBUG)
+			System.out.println("finding: [" + className + "]"); //$NON-NLS-1$ //$NON-NLS-2$
+		try {
+			IType type = fProject.findType(className.replace('$', '.'));
+			if (type != null) {
+				IPath path = null;
+				IResource resource = type.getResource();
+
+				if (resource != null)
+					path = resource.getLocation();
+				if (path == null)
+					path = type.getPath();
+
+				// needs to be compiled before we can load it
+				if ("class".equalsIgnoreCase(path.getFileExtension())) {
+					IFile file = null;
+
+					if (resource != null && resource.getType() == IResource.FILE)
+						file = (IFile) resource;
+					else
+						file = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
+
+					if (file != null && file.isAccessible()) {
+						byte[] bytes = loadBytes(file);
+						return defineClass(className, bytes, 0, bytes.length);
+					}
+				}
+				// Look up the class file based on the output location of the java project
+				else if ("java".equalsIgnoreCase(path.getFileExtension()) && resource != null) { //$NON-NLS-1$
+					if (resource.getProject() != null) {
+						IJavaProject jProject = JavaCore.create(resource.getProject());
+						String outputClass = StringUtils.replace(type.getFullyQualifiedName(), ".", "/").concat(".class"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+						IPath classPath = jProject.getOutputLocation().append(outputClass);
+						IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(classPath);
+						if (file != null && file.isAccessible()) {
+							byte[] bytes = loadBytes(file);
+							return defineClass(className, bytes, 0, bytes.length);
+						}
+					}
+				}
+				else if ("jar".equalsIgnoreCase(path.getFileExtension())) {
+					String expectedFileName = StringUtils.replace(className, ".", "/").concat(".class"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+					byte[] bytes = getCachedInputStream(path.toOSString(), expectedFileName);
+					return defineClass(className, bytes, 0, bytes.length);
+				}
+			}
+		}
+		catch (JavaModelException e) {
+			Logger.logException(e);
+		}
+		return super.findClass(className);
+	}
+
+	/**
+	 * Get the entry from the jarfile
+	 * @param jarFilename the string path of the jarfile
+	 * @param entryName the fully-qualified entry
+	 * @return the bytes for the entry within the jarfile or a byte array of size 0
+	 */
+	private byte[] getCachedInputStream(String jarFilename, String entryName) {
+		ByteArrayOutputStream buffer = null;
+
+		File testFile = new File(jarFilename);
+		if (!testFile.exists())
+			return null;
+
+		ZipFile jarfile = null;
+		try {
+			jarfile = new ZipFile(jarFilename);
+			
+			if (jarfile != null) {
+				ZipEntry zentry = jarfile.getEntry(entryName);
+				if (zentry != null) {
+					InputStream entryInputStream = null;
+					try {
+						entryInputStream = jarfile.getInputStream(zentry);
+					}
+					catch (IOException ioExc) {
+						Logger.logException("JarUtilities: " + jarFilename, ioExc); //$NON-NLS-1$
+					}
+
+					if (entryInputStream != null) {
+						int c;
+						if (zentry.getSize() > 0) {
+							buffer = new ByteArrayOutputStream((int) zentry.getSize());
+						}
+						else {
+							buffer = new ByteArrayOutputStream();
+						}
+						// array dim restriction?
+						byte bytes[] = new byte[2048];
+						try {
+							while ((c = entryInputStream.read(bytes)) >= 0) {
+								buffer.write(bytes, 0, c);
+							}
+						}
+						catch (IOException ioe) {
+							// no cleanup can be done
+						}
+						finally {
+							try {
+								entryInputStream.close();
+							}
+							catch (IOException e) {
+							}
+						}
+					}
+				}
+			}
+		}
+		catch (IOException ioExc) {
+			Logger.logException("JarUtilities: " + jarFilename, ioExc); //$NON-NLS-1$
+		}
+		finally {
+			closeJarFile(jarfile);
+		}
+
+		if (buffer != null) {
+			return buffer.toByteArray();
+		}
+		return new byte[0];
+	}
+
+	/**
+	 * @param file
+	 * @return
+	 */
+	private byte[] loadBytes(IFile file) {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		InputStream in = null;
+		try {
+			in = file.getContents();
+			byte[] buffer = new byte[4096];
+			int read = 0;
+			while ((read = in.read(buffer)) != -1) {
+				out.write(buffer, 0, read);
+			}
+		}
+		catch (CoreException e) {
+			Logger.logException(e);
+		}
+		catch (IOException e) {
+			Logger.logException(e);
+		}
+		finally {
+			try {
+				if (in != null)
+					in.close();
+			}
+			catch (IOException e) {
+			}
+		}
+		return out.toByteArray();
+	}
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/taglib/TaglibHelper.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/taglib/TaglibHelper.java
index 9ce0fc7..0f2925f 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/taglib/TaglibHelper.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/taglib/TaglibHelper.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2004, 2008 IBM Corporation and others.
+ * Copyright (c) 2004, 2009 IBM Corporation 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
@@ -11,16 +11,13 @@
 package org.eclipse.jst.jsp.core.internal.taglib;
 
 
-import java.io.File;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import javax.servlet.jsp.tagext.TagAttributeInfo;
 import javax.servlet.jsp.tagext.TagData;
@@ -30,16 +27,10 @@
 import javax.servlet.jsp.tagext.ValidationMessage;
 import javax.servlet.jsp.tagext.VariableInfo;
 
-import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.ResourcesPlugin;
-import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.Platform;
-import org.eclipse.jdt.core.IClasspathContainer;
-import org.eclipse.jdt.core.IClasspathEntry;
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.JavaModelException;
@@ -83,17 +74,14 @@
 	}
 
 	private IProject fProject = null;
-	private TaglibClassLoader fLoader = null;
+	private ClassLoader fLoader = null;
 
-	private Set fProjectEntries = null;
 	private Map fTranslationProblems = null;
-	private Set fContainerEntries = null;
 	private IJavaProject fJavaProject;
 
 	public TaglibHelper(IProject project) {
+		super();
 		setProject(project);
-		fProjectEntries = new HashSet();
-		fContainerEntries = new HashSet();
 		fTranslationProblems = new HashMap();
 	}
 
@@ -178,7 +166,6 @@
 	 * @param node
 	 */
 	private void addVariables(List results, CMNode node, ITextRegionCollection customTag) {
-
 		List list = ((TLDElementDeclaration) node).getVariables();
 		Iterator it = list.iterator();
 		while (it.hasNext()) {
@@ -240,7 +227,7 @@
 		if (teiClassname == null || teiClassname.length() == 0 || fJavaProject == null)
 			return;
 
-		TaglibClassLoader loader = getClassloader();
+		ClassLoader loader = getClassloader();
 
 		Class teiClass = null;
 		try {
@@ -611,207 +598,14 @@
 		return tagDataTable;
 	}
 
-	private TaglibClassLoader getClassloader() {
+	private ClassLoader getClassloader() {
 		if (fLoader == null) {
-			fLoader = new TaglibClassLoader(this.getClass().getClassLoader());
-			fProjectEntries.clear();
-			fContainerEntries.clear();
-			addClasspathEntriesForProject(getProject(), fLoader);
+			fLoader = new BuildPathClassLoader(this.getClass().getClassLoader(), fJavaProject);
 		}
 		return fLoader;
 	}
 
 	/**
-	 * @param loader
-	 */
-	private void addClasspathEntriesForProject(IProject p, TaglibClassLoader loader) {
-
-		// avoid infinite recursion and closed project
-		if (!p.isAccessible() || fProjectEntries.contains(p.getFullPath().toString()))
-			return;
-		fProjectEntries.add(p.getFullPath().toString());
-
-		// add things on classpath that we are interested in
-		try {
-			if (p.hasNature(JavaCore.NATURE_ID)) {
-
-				IJavaProject project = JavaCore.create(p);
-
-				try {
-					IClasspathEntry[] entries = project.getResolvedClasspath(true);
-					addDefaultDirEntry(loader, project);
-					addClasspathEntries(loader, project, entries);
-				}
-				catch (JavaModelException e) {
-					Logger.logException(e);
-				}
-			}
-		}
-		catch (CoreException e) {
-			Logger.logException(e);
-		}
-	}
-
-	private void addClasspathEntries(TaglibClassLoader loader, IJavaProject project, IClasspathEntry[] entries) throws JavaModelException {
-		IClasspathEntry entry;
-		for (int i = 0; i < entries.length; i++) {
-
-			entry = entries[i];
-			if (DEBUG)
-				System.out.println("current entry is: " + entry); //$NON-NLS-1$
-
-			switch (entry.getEntryKind()) {
-				case IClasspathEntry.CPE_SOURCE :
-					addSourceEntry(loader, entry);
-					break;
-				case IClasspathEntry.CPE_LIBRARY :
-					addLibraryEntry(loader, entry.getPath());
-					break;
-				case IClasspathEntry.CPE_PROJECT :
-					addProjectEntry(loader, entry);
-					break;
-				case IClasspathEntry.CPE_VARIABLE :
-					addVariableEntry(loader, entry);
-					break;
-				case IClasspathEntry.CPE_CONTAINER :
-					addContainerEntry(loader, project, entry);
-					break;
-			}
-		}
-	}
-
-	/**
-	 * @param loader
-	 * @param entry
-	 */
-	private void addVariableEntry(TaglibClassLoader loader, IClasspathEntry entry) {
-		if (DEBUG)
-			System.out.println(" -> adding variable entry: [" + entry + "]"); //$NON-NLS-1$ //$NON-NLS-2$
-
-		// variable should either be a project or a library entry
-
-		// BUG 169431
-		String variableName = entry.getPath().toString();
-		IPath variablePath = JavaCore.getResolvedVariablePath(entry.getPath());
-		variablePath = JavaCore.getClasspathVariable(variableName);
-
-		// RATLC01076854
-		// variable paths may not exist
-		// in that case null will be returned
-		if (variablePath != null) {
-			if (variablePath.segments().length == 1) {
-				IProject varProj = ResourcesPlugin.getWorkspace().getRoot().getProject(variablePath.toString());
-				if (varProj != null && varProj.exists()) {
-					addClasspathEntriesForProject(varProj, loader);
-					return;
-				}
-			}
-			addLibraryEntry(loader, variablePath);
-		}
-	}
-
-	/**
-	 * @param loader
-	 * @param project
-	 * @param entry
-	 * @throws JavaModelException
-	 */
-	private void addContainerEntry(TaglibClassLoader loader, IJavaProject project, IClasspathEntry entry) throws JavaModelException {
-
-		IClasspathContainer container = JavaCore.getClasspathContainer(entry.getPath(), project);
-		if (container != null) {
-			String uniqueProjectAndContainerPath = project.getProject().getFullPath().append(container.getPath()).toString();
-			/*
-			 * Avoid infinite recursion, but track containers for each project
-			 * separately as they may return different values. This may mean
-			 * indexing JREs multiple times, however.
-			 */
-			if (!fContainerEntries.contains(uniqueProjectAndContainerPath)) {
-				fContainerEntries.add(uniqueProjectAndContainerPath);
-
-				IClasspathEntry[] cpes = container.getClasspathEntries();
-				// recursive call here
-				addClasspathEntries(loader, project, cpes);
-			}
-		}
-	}
-
-	/**
-	 * @param loader
-	 * @param entry
-	 */
-	private void addProjectEntry(TaglibClassLoader loader, IClasspathEntry entry) {
-		if (DEBUG)
-			System.out.println(" -> project entry: [" + entry + "]"); //$NON-NLS-1$ //$NON-NLS-2$
-
-		IPath path = entry.getPath();
-		IProject referenceProject = ResourcesPlugin.getWorkspace().getRoot().getProject(path.segment(0));
-		if (referenceProject != null && referenceProject.isAccessible()) {
-			addClasspathEntriesForProject(referenceProject, loader);
-		}
-	}
-
-	/**
-	 * @param loader
-	 * @param project
-	 * @param projectLocation
-	 * @throws JavaModelException
-	 */
-	private void addDefaultDirEntry(TaglibClassLoader loader, IJavaProject project) throws JavaModelException {
-		// add default bin directory for the project
-		IPath outputPath = project.getOutputLocation();
-		loader.addFolder(outputPath);
-	}
-
-	/**
-	 * @param loader
-	 * @param entry
-	 */
-	private void addLibraryEntry(TaglibClassLoader loader, IPath libPath) {
-		String libPathString = libPath.toString();
-		File file = new File(libPathString);
-
-		if (file.exists()) {
-			if (file.isDirectory()) {
-				loader.addDirectory(libPathString);
-			}
-			else {
-				loader.addJar(libPathString);
-			}
-		}
-		else {
-			if (libPath.segmentCount() > 1) {
-				IFile ifile = ResourcesPlugin.getWorkspace().getRoot().getFile(libPath);
-				if (ifile != null && ifile.isAccessible()) {
-					loader.addFile(libPath);
-				}
-				else {
-					IFolder ifolder = ResourcesPlugin.getWorkspace().getRoot().getFolder(libPath);
-					if (ifolder != null && ifolder.isAccessible()) {
-						loader.addFolder(libPath);
-					}
-				}
-			}
-			else {
-				loader.addFolder(libPath);
-			}
-		}
-	}
-
-	/**
-	 * @param loader
-	 * @param entry
-	 */
-	private void addSourceEntry(TaglibClassLoader loader, IClasspathEntry entry) {
-		// add bin directory for specific entry if it has
-		// one
-		IPath outputLocation = entry.getOutputLocation();
-		if (outputLocation != null) {
-			loader.addFolder(outputLocation);
-		}
-	}
-
-	/**
 	 * @return Returns the fModelQuery.
 	 */
 	public ModelQuery getModelQuery(IDocument doc) {
@@ -880,4 +674,14 @@
 			}
 		}
 	}
+
+	/**
+	 * 
+	 */
+	public void dispose() {
+		fLoader = null;
+		fJavaProject = null;
+		fProject = null;
+		fTranslationProblems = null;
+	}
 }
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/taglib/TaglibHelperCache.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/taglib/TaglibHelperCache.java
index acf1e30..05f086d 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/taglib/TaglibHelperCache.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/taglib/TaglibHelperCache.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2008 IBM Corporation and others.
+ * Copyright (c) 2005, 2009 IBM Corporation 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
@@ -129,9 +129,10 @@
         }
         if(fHelpers.size() > MAX_SIZE) {
             // one too many, remove last
-            Object removed = fHelpers.remove(fHelpers.size()-1);
+        	Entry removed = (Entry) fHelpers.remove(fHelpers.size()-1);
+        	removed.getHelper().dispose();
             if(DEBUG) {
-            	Logger.log(Logger.INFO, "(-) TaglibHelperCache removed: " + removed); //$NON-NLS-1$
+            	Logger.log(Logger.INFO, "(-) TaglibHelperCache removed: " + removed.getProjectName()); //$NON-NLS-1$
                 printCacheContents();
             }
         }
@@ -144,6 +145,7 @@
         while(it.hasNext()) {
             entry = (Entry)it.next();
             if(entry.getProjectName().equals(projectName)) {
+            	entry.getHelper().dispose();
                 fHelpers.remove(entry);
                 if(DEBUG) { 
                     Logger.log(Logger.INFO, "(-) TaglibHelperCache removed: " + entry); //$NON-NLS-1$