Skip to content

Commit

Permalink
Cleanup and work for nonascii friends
Browse files Browse the repository at this point in the history
  • Loading branch information
Thrameos committed Nov 27, 2024
1 parent 3ef37ba commit 4766f12
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 218 deletions.
32 changes: 18 additions & 14 deletions jpype/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ def startJVM(
*jvmargs: str,
jvmpath: typing.Optional[_PathOrStr] = None,
classpath: typing.Union[typing.Sequence[_PathOrStr], _PathOrStr, None] = None,
modulepath: typing.Union[typing.Sequence[_PathOrStr], _PathOrStr, None] = None,
ignoreUnrecognized: bool = False,
convertStrings: bool = False,
interrupt: bool = not interactive(),
Expand Down Expand Up @@ -214,6 +213,10 @@ def startJVM(
TypeError: if a keyword argument conflicts with the positional arguments.
"""

# Code for 1.6
# modulepath: typing.Union[typing.Sequence[_PathOrStr], _PathOrStr, None] = None,

if _jpype.isStarted():
raise OSError('JVM is already started')
global _JVM_started
Expand Down Expand Up @@ -286,19 +289,19 @@ def startJVM(
elif system_class_loader:
# https://bugs.openjdk.org/browse/JDK-8079633?jql=text%20~%20%22ParseUtil%22
raise ValueError("system classloader cannot be specified with non ascii characters in the classpath")
elif support_lib.isascii():
# ok, setup the jpype system classloader and add to the path after startup
# this guarentees all classes have the same permissions as they did in the past
extra_jvm_args += [
'-Djava.system.class.loader=org.jpype.classloader.JpypeSystemClassLoader',
'-Djava.class.path=%s'%support_lib
]
else:
# We are screwed no matter what we try or do.
# Unfortunately the jdk maintainers don't seem to care either.
# This bug is almost 10 years old and spans 16 jdk versions and counting.
# https://bugs.openjdk.org/browse/JDK-8079633?jql=text%20~%20%22ParseUtil%22
raise ValueError("jpype jar must be ascii to add to the system class path")
if not support_lib.isascii():
import tempfile
import shutil
tmp = tempfile.gettempdir()
if not tmp.isascii():
raise ValueError("Unable to create ascii temp directory. Clear TEMPDIR, TEMP, and TMP environment variables")
support_lib2 = os.path.join(tmp, "org.jpype.jar")
shutil.copyfile(support_lib, support_lib2)
support_lib = support_lib2

extra_jvm_args += ['-Djava.system.class.loader=org.jpype.classloader.DynamicClassLoader' ]
extra_jvm_args += ['-Djava.class.path=%s'%os.path.join(tmp, "org.jpype.jar") ]

if agent:
extra_jvm_args += ['-javaagent:' + support_lib]
Expand Down Expand Up @@ -344,8 +347,9 @@ def startJVM(
if late_load and classpath:
# now we can add to the system classpath
cl = _jpype.JClass("java.lang.ClassLoader").getSystemClassLoader()
from pathlib import Path
for cp in _expandClassPath(classpath):
cl.addPath(_jpype._java_lang_String(cp))
cl.addFile(Path(cp))


def initializeResources():
Expand Down
36 changes: 27 additions & 9 deletions native/java/org/jpype/classloader/DynamicClassLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
Expand All @@ -26,15 +27,28 @@
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class DynamicClassLoader extends ClassLoader
public class DynamicClassLoader extends URLClassLoader
{

List<URLClassLoader> loaders = new LinkedList<>();
HashMap<String, ArrayList<URL>> map = new HashMap<>();

public DynamicClassLoader(ClassLoader parent)
{
super(parent);
super(new URL[0], parent);
}


public void addFile(String path) throws Throwable
{
addURL(Paths.get(path).toAbsolutePath().toUri().toURL());
}

// this is required to add a Java agent even if it is already in the path
@SuppressWarnings("unused")
private void appendToClassPathForInstrumentation(String path) throws Throwable
{
addURL(Paths.get(path).toAbsolutePath().toUri().toURL());
}

public int getCode()
Expand Down Expand Up @@ -77,7 +91,7 @@ public FileVisitResult visitFileFailed(Path file, IOException exc)
}
});

loaders.add(new URLClassLoader(urls.toArray(new URL[urls.size()])));
loaders.add(new URLClassLoader(urls.toArray(new URL[0])));
}

public void addFile(Path path) throws FileNotFoundException
Expand Down Expand Up @@ -120,7 +134,7 @@ public Class findClass(String name) throws ClassNotFoundException, ClassFormatEr
try
{
URLConnection connection = url.openConnection();
try ( InputStream is = connection.getInputStream())
try (InputStream is = connection.getInputStream())
{
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int bytes;
Expand All @@ -146,6 +160,11 @@ public URL getResource(String name)
URL url = this.getParent().getResource(name);
if (url != null)
return url;

url = super.getResource(name);
if (url != null)
return url;

for (ClassLoader cl : this.loaders)
{
url = cl.getResource(name);
Expand All @@ -164,12 +183,11 @@ public URL getResource(String name)
public Enumeration<URL> getResources(String name) throws IOException
{
ArrayList<URL> out = new ArrayList<>();
Enumeration<URL> urls = getParent().getResources(name);
out.addAll(Collections.list(urls));
out.addAll(Collections.list(getParent().getResources(name)));
out.addAll(Collections.list(super.getResources(name)));
for (URLClassLoader cl : this.loaders)
{
urls = cl.findResources(name);
out.addAll(Collections.list(urls));
out.addAll(Collections.list(cl.findResources(name)));
}
// Both with and without / should generate the same result
if (name.endsWith("/"))
Expand Down Expand Up @@ -201,7 +219,7 @@ public void scanJar(Path p1)
return;
if (Files.isDirectory(p1))
return;
try ( JarFile jf = new JarFile(p1.toFile()))
try (JarFile jf = new JarFile(p1.toFile()))
{
Enumeration<JarEntry> entries = jf.entries();
URI abs = p1.toAbsolutePath().toUri();
Expand Down
149 changes: 0 additions & 149 deletions native/java/org/jpype/classloader/JPypeClassLoader.java

This file was deleted.

43 changes: 0 additions & 43 deletions native/java/org/jpype/classloader/JpypeSystemClassLoader.java

This file was deleted.

6 changes: 3 additions & 3 deletions test/jpypetest/test_startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def testNonASCIIPath(self):
"""
jpype.startJVM(jvmpath=Path(self.jvmpath), classpath="test/jar/unicode_à😎/sample_package.jar")
cl = jpype.JClass("java.lang.ClassLoader").getSystemClassLoader()
self.assertEqual(type(cl), jpype.JClass("org.jpype.classloader.JpypeSystemClassLoader"))
self.assertEqual(type(cl), jpype.JClass("org.jpype.classloader.DynamicClassLoader"))
assert dir(jpype.JPackage('org.jpype.sample_package')) == ['A', 'B']


Expand All @@ -162,7 +162,7 @@ def testOldStyleNonASCIIPath(self):
"""
jpype.startJVM("-Djava.class.path=test/jar/unicode_à😎/sample_package.jar", jvmpath=Path(self.jvmpath))
cl = jpype.JClass("java.lang.ClassLoader").getSystemClassLoader()
self.assertEqual(type(cl), jpype.JClass("org.jpype.classloader.JpypeSystemClassLoader"))
self.assertEqual(type(cl), jpype.JClass("org.jpype.classloader.DynamicClassLoader"))
assert dir(jpype.JPackage('org.jpype.sample_package')) == ['A', 'B']

def testNonASCIIPathWithSystemClassLoader(self):
Expand Down Expand Up @@ -207,7 +207,7 @@ def testDefaultSystemClassLoader(self):
# we introduce no behavior change unless absolutely necessary
jpype.startJVM(jvmpath=Path(self.jvmpath))
cl = jpype.JClass("java.lang.ClassLoader").getSystemClassLoader()
self.assertNotEqual(type(cl), jpype.JClass("org.jpype.classloader.JpypeSystemClassLoader"))
self.assertNotEqual(type(cl), jpype.JClass("org.jpype.classloader.DynamicClassLoader"))

def testServiceWithNonASCIIPath(self):
jpype.startJVM(
Expand Down

0 comments on commit 4766f12

Please sign in to comment.