Skip to content

Commit

Permalink
* Replace calls to Class.getResource() with `Loader.findResource()…
Browse files Browse the repository at this point in the history
…` to work around issues with JPMS ([pull #276](#276))

 * Enhance `Loader.findResources()` with `Class.getResource()` and search among parent packages
 * Take shortest common package name among all user classes for the default output path of `Builder`
  • Loading branch information
saudet committed Dec 10, 2018
1 parent 3ebcd82 commit f3a7d76
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 27 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@

* Replace calls to `Class.getResource()` with `Loader.findResource()` to work around issues with JPMS ([pull #276](https://github.com/bytedeco/javacpp/pull/276))
* Enhance `Loader.findResources()` with `Class.getResource()` and search among parent packages
* Take shortest common package name among all user classes for the default output path of `Builder`
* Add `Bfloat16Indexer` to access `short` arrays as `bfloat16` floating point numbers
* When `Indexer.sizes.length != 3`, return -1 for `rows()`, `cols()`, `width()`, `height()`, and `channels()` ([pull #275](https://github.com/bytedeco/javacpp/pull/275))
* Synchronize `Loader.cacheResources()` on `Runtime` to avoid `OverlappingFileLockException` with multiple class loaders ([issue bytedeco/javacpp-presets#650](https://github.com/bytedeco/javacpp-presets/issues/650))
Expand Down
77 changes: 56 additions & 21 deletions src/main/java/org/bytedeco/javacpp/Loader.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
Expand Down Expand Up @@ -320,7 +319,7 @@ public static File cacheResource(String name) throws IOException {
* @see #cacheResource(URL)
*/
public static File cacheResource(Class cls, String name) throws IOException {
return cacheResource(cls.getResource(name));
return cacheResource(findResource(cls, name));
}

/**
Expand Down Expand Up @@ -565,7 +564,7 @@ public static File extractResource(String name, File directory,
*/
public static File extractResource(Class cls, String name, File directory,
String prefix, String suffix) throws IOException {
return extractResource(cls.getResource(name), directory, prefix, suffix);
return extractResource(findResource(cls, name), directory, prefix, suffix);
}

/**
Expand Down Expand Up @@ -692,32 +691,68 @@ public static File extractResource(URL resourceURL, File directoryOrFile,
return file;
}

/** Returns {@code findResources(cls, name, 1)[0]} or null if none. */
public static URL findResource(Class cls, String name) throws IOException {
URL[] url = findResources(cls, name, 1);
return url.length > 0 ? url[0] : null;
}

/** Returns {@code findResources(cls, name, -1)}. */
public static URL[] findResources(Class cls, String name) throws IOException {
return findResources(cls, name, -1);
}

/**
* Finds by name resources using the {@link ClassLoader} of the specified {@link Class}.
* Names not prefixed with '/' are considered relative to the Class.
* Finds by name resources using the {@link Class} and its {@link ClassLoader}.
* Names not prefixed with '/' are considered in priority relative to the Class,
* but parent packages, including the default one, also get searched.
*
* @param cls the Class from whose ClassLoader to load resources
* @param name of the resources passed to {@link ClassLoader#getResources(String)}
* @param name of the resources passed to {@link Class#getResource(String)} and {@link ClassLoader#getResources(String)}
* @param maxLength of the array to return, or -1 for no limit
* @return URLs to the resources
* @throws IOException
*/
public static URL[] findResources(Class cls, String name) throws IOException {
public static URL[] findResources(Class cls, String name, int maxLength) throws IOException {
while (name.contains("//")) {
name = name.replace("//", "/");
}

// Under JPMS, Class.getResource() and ClassLoader.getResources() do not return the same URLs
URL url = cls.getResource(name);
if (url != null && maxLength == 1) {
return new URL[] {url};
}

String path = "";
if (!name.startsWith("/")) {
String s = cls.getName().replace('.', '/');
int n = s.lastIndexOf('/');
if (n >= 0) {
name = s.substring(0, n + 1) + name;
path = s.substring(0, n + 1);
}
} else {
name = name.substring(1);
}
Enumeration<URL> urls = cls.getClassLoader().getResources(name);
Enumeration<URL> urls = cls.getClassLoader().getResources(path + name);
ArrayList<URL> array = new ArrayList<URL>();
while (urls.hasMoreElements()) {
array.add(urls.nextElement());
if (url != null) {
array.add(url);
}
while (url == null && !urls.hasMoreElements() && path.length() > 0) {
int n = path.lastIndexOf('/', path.length() - 2);
if (n >= 0) {
path = path.substring(0, n + 1);
} else {
path = "";
}
urls = cls.getClassLoader().getResources(path + name);
}
while (urls.hasMoreElements() && array.size() < maxLength) {
url = urls.nextElement();
if (!array.contains(url)) {
array.add(url);
}
}
return array.toArray(new URL[array.size()]);
}
Expand Down Expand Up @@ -1098,18 +1133,18 @@ public static URL[] findLibrary(Class cls, ClassProperties properties, String li
}
String subdir = (resource == null ? "" : "/" + resource) + platform
+ (extension == null ? "" : extension) + "/";
URL u = cls.getResource(subdir + styles[i]);
if (u != null) {
if (reference) {
try {
try {
URL u = findResource(cls, subdir + styles[i]);
if (u != null) {
if (reference) {
u = new URL(u + "#" + styles2[i]);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
if (!urls.contains(u)) {
urls.add(u);
}
}
if (!urls.contains(u)) {
urls.add(u);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Expand All @@ -1128,7 +1163,7 @@ public static URL[] findLibrary(Class cls, ClassProperties properties, String li
if (!urls.contains(u)) {
urls.add(k++, u);
}
} catch (MalformedURLException ex) {
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
Expand Down
24 changes: 20 additions & 4 deletions src/main/java/org/bytedeco/javacpp/tools/Builder.java
Original file line number Diff line number Diff line change
Expand Up @@ -460,11 +460,13 @@ int compile(String[] sourceFilenames, String outputFilename, ClassProperties pro
}

/**
* Generates a C++ source file for classes, and compiles everything in
* Generates C++ source files for classes, and compiles everything in
* one shared library when {@code compile == true}.
*
* @param classes the Class objects as input to Generator
* @param outputName the output name of the shared library
* @param first of the batch, so generate jnijavacpp.cpp
* @param last of the batch, so delete jnijavacpp.cpp
* @return the actual File generated, either the compiled library or its source
* @throws IOException
* @throws InterruptedException
Expand All @@ -483,9 +485,23 @@ File[] generateAndCompile(Class[] classes, String outputName, boolean first, boo
if (outputPath == null) {
URI uri = null;
try {
String resourceName = '/' + classes[classes.length - 1].getName().replace('.', '/') + ".class";
String resourceURL = classes[classes.length - 1].getResource(resourceName).toString();
uri = new URI(resourceURL.substring(0, resourceURL.lastIndexOf('/') + 1));
String resourceName = '/' + classes[0].getName().replace('.', '/') + ".class";
String resourceURL = Loader.findResource(classes[0], resourceName).toString();
String packageURI = resourceURL.substring(0, resourceURL.lastIndexOf('/') + 1);
for (int i = 1; i < classes.length; i++) {
// Use shortest common package name among all classes as default output path
String resourceName2 = '/' + classes[i].getName().replace('.', '/') + ".class";
String resourceURL2 = Loader.findResource(classes[i], resourceName2).toString();
String packageURI2 = resourceURL2.substring(0, resourceURL2.lastIndexOf('/') + 1);

String longest = packageURI2.length() > packageURI.length() ? packageURI2 : packageURI;
String shortest = packageURI2.length() < packageURI.length() ? packageURI2 : packageURI;
while (!longest.startsWith(shortest) && shortest.lastIndexOf('/') > 0) {
shortest = shortest.substring(0, shortest.lastIndexOf('/'));
}
packageURI = shortest;
}
uri = new URI(packageURI);
boolean isFile = "file".equals(uri.getScheme());
File classPath = new File(classScanner.getClassLoader().getPaths()[0]).getCanonicalFile();
// If our class is not a file, use first path of the user class loader as base for our output path
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/bytedeco/javacpp/tools/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -3693,7 +3693,7 @@ public File parse(File outputDirectory, String[] classPath, Class cls) throws IO
String javaText = text + "import static " + global + ".*;\n"
+ (prevd != null && prevd.comment ? prevd.text : "")
+ d.text.replace("public static class " + d.type.javaName + " ",
"@Properties(inherit = " + cls.getSimpleName() + ".class)\n"
"@Properties(inherit = " + cls.getCanonicalName() + ".class)\n"
+ "public class " + d.type.javaName + " ");
Files.write(javaFile.toPath(), encoding != null ? javaText.getBytes(encoding) : javaText.getBytes());
prevd = null;
Expand Down
2 changes: 1 addition & 1 deletion src/test/java/org/bytedeco/javacpp/BuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public class BuilderTest implements BuildEnabled, LoadEnabled {
Class c = BuilderTest.class;
String[] extensions = {"", "-ext1", "-ext2"};
for (String extension : extensions) {
URL u = c.getResource(Loader.getPlatform() + extension);
URL u = Loader.findResource(c, Loader.getPlatform() + extension);
if (u != null) {
for (File f : new File(u.toURI()).listFiles()) {
f.delete();
Expand Down

0 comments on commit f3a7d76

Please sign in to comment.