/******************************************************************************* * 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); int offset = -1; if (type == null && (offset = className.indexOf('$')) != -1) { // Internal classes from source files must be referenced by . instead of $ String cls = className.substring(0, offset) + className.substring(offset).replace('$', '.'); type = fProject.findType(cls); } 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(); } }