<h2>Changes</h2> | <h2>Changes</h2> | ||||
<p>-version 3.23 on MMM DD, YYYY | |||||
<ul> | |||||
<li>Fix leaking file handlers in ClassPool and removed ClassPath.close(). Github issue #165. | |||||
</ul> | |||||
</p> | |||||
<p>-version 3.22 on October 10, 2017 | <p>-version 3.22 on October 10, 2017 | ||||
<ul> | <ul> |
<build> | <build> | ||||
<sourceDirectory>src/main/</sourceDirectory> | <sourceDirectory>src/main/</sourceDirectory> | ||||
<testSourceDirectory>src/test/</testSourceDirectory> | <testSourceDirectory>src/test/</testSourceDirectory> | ||||
<testResources> | |||||
<testResource> | |||||
<directory>src/test/resources</directory> | |||||
</testResource> | |||||
</testResources> | |||||
<plugins> | <plugins> | ||||
<plugin> | <plugin> | ||||
<groupId>org.apache.maven.plugins</groupId> | <groupId>org.apache.maven.plugins</groupId> | ||||
<includes> | <includes> | ||||
<include>javassist/JvstTest.java</include> | <include>javassist/JvstTest.java</include> | ||||
</includes> | </includes> | ||||
<forkMode>once</forkMode> | |||||
<additionalClasspathElements> | |||||
<additionalClasspathElement>resources</additionalClasspathElement> | |||||
</additionalClasspathElements> | |||||
<forkMode>once</forkMode> | |||||
<workingDirectory>runtest</workingDirectory> | <workingDirectory>runtest</workingDirectory> | ||||
</configuration> | </configuration> | ||||
</plugin> | </plugin> |
this.classfile = classfile; | this.classfile = classfile; | ||||
} | } | ||||
/** | |||||
* Closes this class path. | |||||
*/ | |||||
public void close() {} | |||||
public String toString() { | public String toString() { | ||||
return "byte[]:" + classname; | return "byte[]:" + classname; | ||||
} | } |
return thisClass.getResource(filename); | return thisClass.getResource(filename); | ||||
} | } | ||||
/** | |||||
* Does nothing. | |||||
*/ | |||||
public void close() { | |||||
} | |||||
public String toString() { | public String toString() { | ||||
return thisClass.getName() + ".class"; | return thisClass.getName() + ".class"; | ||||
} | } |
* @return null if the specified class file could not be found. | * @return null if the specified class file could not be found. | ||||
*/ | */ | ||||
URL find(String classname); | URL find(String classname); | ||||
/** | |||||
* This method is invoked when the <code>ClassPath</code> object is | |||||
* detached from the search path. It will be an empty method in most of | |||||
* classes. | |||||
*/ | |||||
void close(); | |||||
} | } |
import java.io.OutputStream; | import java.io.OutputStream; | ||||
import java.net.MalformedURLException; | import java.net.MalformedURLException; | ||||
import java.net.URL; | import java.net.URL; | ||||
import java.util.ArrayList; | |||||
import java.util.Collections; | |||||
import java.util.List; | |||||
import java.util.jar.JarEntry; | import java.util.jar.JarEntry; | ||||
import java.util.jar.JarFile; | import java.util.jar.JarFile; | ||||
return null; | return null; | ||||
} | } | ||||
public void close() {} | |||||
public String toString() { | public String toString() { | ||||
return directory; | return directory; | ||||
} | } | ||||
return null; // not found | return null; // not found | ||||
} | } | ||||
public void close() { | |||||
if (jars != null) | |||||
for (int i = 0; i < jars.length; i++) | |||||
jars[i].close(); | |||||
} | |||||
} | } | ||||
final class JarClassPath implements ClassPath { | final class JarClassPath implements ClassPath { | ||||
JarFile jarfile; | |||||
List<String> jarfileEntries; | |||||
String jarfileURL; | String jarfileURL; | ||||
JarClassPath(String pathname) throws NotFoundException { | JarClassPath(String pathname) throws NotFoundException { | ||||
JarFile jarfile = null; | |||||
try { | try { | ||||
jarfile = new JarFile(pathname); | jarfile = new JarFile(pathname); | ||||
jarfileEntries = new ArrayList<String>(); | |||||
for (JarEntry je:Collections.list(jarfile.entries())) | |||||
if (je.getName().endsWith(".class")) | |||||
jarfileEntries.add(je.getName()); | |||||
jarfileURL = new File(pathname).getCanonicalFile() | jarfileURL = new File(pathname).getCanonicalFile() | ||||
.toURI().toURL().toString(); | |||||
.toURI().toURL().toString(); | |||||
return; | return; | ||||
} catch (IOException e) {} | |||||
finally { | |||||
if (null != jarfile) | |||||
try { | |||||
jarfile.close(); | |||||
} catch (IOException e) {} | |||||
} | } | ||||
catch (IOException e) {} | |||||
throw new NotFoundException(pathname); | throw new NotFoundException(pathname); | ||||
} | } | ||||
@Override | |||||
public InputStream openClassfile(String classname) | public InputStream openClassfile(String classname) | ||||
throws NotFoundException | |||||
throws NotFoundException | |||||
{ | { | ||||
try { | |||||
String jarname = classname.replace('.', '/') + ".class"; | |||||
JarEntry je = jarfile.getJarEntry(jarname); | |||||
if (je != null) | |||||
return jarfile.getInputStream(je); | |||||
else | |||||
return null; // not found | |||||
} | |||||
catch (IOException e) {} | |||||
throw new NotFoundException("broken jar file?: " | |||||
+ jarfile.getName()); | |||||
URL jarURL = find(classname); | |||||
if (null != jarURL) | |||||
try { | |||||
return jarURL.openConnection().getInputStream(); | |||||
} | |||||
catch (IOException e) { | |||||
throw new NotFoundException("broken jar file?: " | |||||
+ classname); | |||||
} | |||||
return null; | |||||
} | } | ||||
@Override | |||||
public URL find(String classname) { | public URL find(String classname) { | ||||
String jarname = classname.replace('.', '/') + ".class"; | String jarname = classname.replace('.', '/') + ".class"; | ||||
JarEntry je = jarfile.getJarEntry(jarname); | |||||
if (je != null) | |||||
if (jarfileEntries.contains(jarname)) | |||||
try { | try { | ||||
return new URL("jar:" + jarfileURL + "!/" + jarname); | |||||
return new URL(String.format("jar:%s!/%s", jarfileURL, jarname)); | |||||
} | } | ||||
catch (MalformedURLException e) {} | catch (MalformedURLException e) {} | ||||
return null; // not found | return null; // not found | ||||
} | } | ||||
public void close() { | |||||
try { | |||||
jarfile.close(); | |||||
jarfile = null; | |||||
} | |||||
catch (IOException e) {} | |||||
} | |||||
@Override | |||||
public String toString() { | public String toString() { | ||||
return jarfile == null ? "<null>" : jarfile.toString(); | |||||
return jarfileURL == null ? "<null>" : jarfileURL.toString(); | |||||
} | } | ||||
} | } | ||||
else | else | ||||
list = list.next; | list = list.next; | ||||
} | } | ||||
cp.close(); | |||||
} | } | ||||
public ClassPath appendSystemPath() { | public ClassPath appendSystemPath() { |
return url; | return url; | ||||
} | } | ||||
} | } | ||||
/** | |||||
* Closes this class path. | |||||
*/ | |||||
public void close() { | |||||
clref = null; | |||||
} | |||||
} | } |
return null; | return null; | ||||
} | } | ||||
/** | |||||
* Closes this class path. | |||||
*/ | |||||
public void close() {} | |||||
/** | /** | ||||
* Reads a class file on an http server. | * Reads a class file on an http server. | ||||
* | * |
package javassist; | package javassist; | ||||
import junit.framework.*; | import junit.framework.*; | ||||
import java.io.File; | |||||
import java.io.FileInputStream; | import java.io.FileInputStream; | ||||
import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
import javassist.bytecode.*; | import javassist.bytecode.*; | ||||
assertTrue("[class path: ]".equals(pool.toString())); | assertTrue("[class path: ]".equals(pool.toString())); | ||||
} | } | ||||
public void testReleaseJarClassPathFileHandle() throws Exception { | |||||
String jarFileName = "./empty.jar"; | |||||
ClassLoader classLoader = getClass().getClassLoader(); | |||||
File jarFile = new File(classLoader.getResource(jarFileName).getFile()); | |||||
assertTrue(jarFile.exists()); | |||||
// Prepare class pool and force it to open the Jar file | |||||
ClassPool pool = ClassPool.getDefault(); | |||||
ClassPath cp = pool.appendClassPath(jarFile.getAbsolutePath()); | |||||
assertNull(cp.openClassfile("nothere.Dummy")); | |||||
// Assert that it is possible to delete the jar file. | |||||
// On Windows deleting an open file will fail, while on on Mac/Linux this is always possible. | |||||
// This check will thus only fail on Windos if the file is still open. | |||||
assertTrue(jarFile.delete()); | |||||
} | |||||
public void testJarClassPath() throws Exception { | |||||
// TODO: Verify that classes can be loaded from a JarClassPath | |||||
} | |||||
public void testSubtype() throws Exception { | public void testSubtype() throws Exception { | ||||
CtClass cc = sloader.get("test1.Subtype"); | CtClass cc = sloader.get("test1.Subtype"); | ||||
assertTrue(cc.subtypeOf(cc)); | assertTrue(cc.subtypeOf(cc)); |
This directory contains files used by the unit tests. | |||||
empty.jar: | |||||
An empty, but valid, jar file. | |||||