Repository: deepjavalibrary/djl
Pull request #1445 (2022-01-04 19:32:30)

[api] Fix NPE bug in getContextClassLoader().
Change-Id: Icae3ae41116bcb79c8d665794cab787848f9583e

## Description ##

Brief description of what this PR is about

- If this change is a backward incompatible change, why must this change be made?
- Interesting edge cases to note here


codecov-commenter: # [Codecov](https://codecov.io/gh/deepjavalibrary/djl/pull/1445?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) Report
> Merging [#1445](https://codecov.io/gh/deepjavalibrary/djl/pull/1445?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) (e1a0241) into [master](https://codecov.io/gh/deepjavalibrary/djl/commit/bb5073f7ffc33f1883621fc1783dbb01e70b418f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) (bb5073f) will **decrease** coverage by `1.43%`.
> The diff coverage is `47.98%`.

[![Impacted file tree graph](https://codecov.io/gh/deepjavalibrary/djl/pull/1445/graphs/tree.svg?width=650&height=150&src=pr&token=mDKVNwFXRV&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary)](https://codecov.io/gh/deepjavalibrary/djl/pull/1445?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary)

```diff
@@             Coverage Diff              @@
##             master    #1445      +/-   ##
============================================
- Coverage     72.08%   70.65%   -1.44%     
- Complexity     5126     5297     +171     
============================================
  Files           473      493      +20     
  Lines         21970    23221    +1251     
  Branches       2351     2533     +182     
============================================
+ Hits          15838    16406     +568     
- Misses         4925     5537     +612     
- Partials       1207     1278      +71     
```


| [Impacted Files](https://codecov.io/gh/deepjavalibrary/djl/pull/1445?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) | Coverage Δ | |
|---|---|---|
| [api/src/main/java/ai/djl/modality/cv/Image.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1445/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L0ltYWdlLmphdmE=) | `69.23% <ø> (-4.11%)` | :arrow_down: |
| [...i/djl/modality/cv/translator/BigGANTranslator.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1445/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvQmlnR0FOVHJhbnNsYXRvci5qYXZh) | `21.42% <ø> (-5.24%)` | :arrow_down: |
| [...odality/cv/translator/BigGANTranslatorFactory.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1445/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvQmlnR0FOVHJhbnNsYXRvckZhY3RvcnkuamF2YQ==) | `33.33% <0.00%> (+8.33%)` | :arrow_up: |
| [...nslator/InstanceSegmentationTranslatorFactory.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1445/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvSW5zdGFuY2VTZWdtZW50YXRpb25UcmFuc2xhdG9yRmFjdG9yeS5qYXZh) | `14.28% <0.00%> (-3.90%)` | :arrow_down: |
| [.../modality/cv/translator/YoloTranslatorFactory.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1445/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvWW9sb1RyYW5zbGF0b3JGYWN0b3J5LmphdmE=) | `8.33% <0.00%> (-1.67%)` | :arrow_down: |
| [...i/djl/modality/cv/translator/YoloV5Translator.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1445/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvWW9sb1Y1VHJhbnNsYXRvci5qYXZh) | `5.69% <0.00%> (ø)` | |
| [...odality/cv/translator/YoloV5TranslatorFactory.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1445/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L2N2L3RyYW5zbGF0b3IvWW9sb1Y1VHJhbnNsYXRvckZhY3RvcnkuamF2YQ==) | `8.33% <0.00%> (-1.67%)` | :arrow_down: |
| [...modality/nlp/embedding/TrainableWordEmbedding.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1445/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL21vZGFsaXR5L25scC9lbWJlZGRpbmcvVHJhaW5hYmxlV29yZEVtYmVkZGluZy5qYXZh) | `45.00% <0.00%> (-3.34%)` | :arrow_down: |
| [...pi/src/main/java/ai/djl/ndarray/BytesSupplier.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1445/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL25kYXJyYXkvQnl0ZXNTdXBwbGllci5qYXZh) | `54.54% <0.00%> (-12.13%)` | :arrow_down: |
| [...i/src/main/java/ai/djl/ndarray/NDArrayAdapter.java](https://codecov.io/gh/deepjavalibrary/djl/pull/1445/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary#diff-YXBpL3NyYy9tYWluL2phdmEvYWkvZGpsL25kYXJyYXkvTkRBcnJheUFkYXB0ZXIuamF2YQ==) | `15.23% <0.00%> (-0.83%)` | :arrow_down: |
| ... and [161 more](https://codecov.io/gh/deepjavalibrary/djl/pull/1445/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/deepjavalibrary/djl/pull/1445?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/deepjavalibrary/djl/pull/1445?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary). Last update [c41b6a0...e1a0241](https://codecov.io/gh/deepjavalibrary/djl/pull/1445?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=deepjavalibrary).


Patch:
diff --git a/api/src/main/java/ai/djl/repository/RepositoryFactoryImpl.java b/api/src/main/java/ai/djl/repository/RepositoryFactoryImpl.java
index a1c0ad3ac..df3fd3683 100644
--- a/api/src/main/java/ai/djl/repository/RepositoryFactoryImpl.java
+++ b/api/src/main/java/ai/djl/repository/RepositoryFactoryImpl.java
@@ -14,6 +14,7 @@ package ai.djl.repository;
 
 import ai.djl.repository.zoo.ModelLoader;
 import ai.djl.repository.zoo.ModelZoo;
+import ai.djl.util.ClassLoaderUtils;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URI;
@@ -137,7 +138,7 @@ class RepositoryFactoryImpl implements RepositoryFactory {
             if (p.startsWith("/")) {
                 p = p.substring(1);
             }
-            URL u = Thread.currentThread().getContextClassLoader().getResource(p);
+            URL u = ClassLoaderUtils.getContextClassLoader().getResource(p);
             if (u == null) {
                 throw new IllegalArgumentException("Resource not found: " + uri);
             }
diff --git a/api/src/main/java/ai/djl/repository/zoo/BaseModelLoader.java b/api/src/main/java/ai/djl/repository/zoo/BaseModelLoader.java
index 39f16d0cd..af6d46076 100644
--- a/api/src/main/java/ai/djl/repository/zoo/BaseModelLoader.java
+++ b/api/src/main/java/ai/djl/repository/zoo/BaseModelLoader.java
@@ -228,7 +228,7 @@ public class BaseModelLoader implements ModelLoader {
 
         String factoryClass = (String) arguments.get("translatorFactory");
         if (factoryClass != null) {
-            ClassLoader cl = Thread.currentThread().getContextClassLoader();
+            ClassLoader cl = ClassLoaderUtils.getContextClassLoader();
             factory = ClassLoaderUtils.initClass(cl, factoryClass);
         }
         return factory;
diff --git a/api/src/main/java/ai/djl/translate/ServingTranslatorFactory.java b/api/src/main/java/ai/djl/translate/ServingTranslatorFactory.java
index 92f84161f..11353777b 100644
--- a/api/src/main/java/ai/djl/translate/ServingTranslatorFactory.java
+++ b/api/src/main/java/ai/djl/translate/ServingTranslatorFactory.java
@@ -20,6 +20,7 @@ import ai.djl.modality.cv.translator.ImageClassificationTranslator;
 import ai.djl.modality.cv.translator.ImageServingTranslator;
 import ai.djl.ndarray.NDList;
 import ai.djl.ndarray.NDManager;
+import ai.djl.util.ClassLoaderUtils;
 import ai.djl.util.Pair;
 import java.io.File;
 import java.io.IOException;
@@ -107,7 +108,7 @@ public class ServingTranslatorFactory implements TranslatorFactory {
                 urls.add(p.toUri().toURL());
             }
 
-            ClassLoader parentCl = Thread.currentThread().getContextClassLoader();
+            ClassLoader parentCl = ClassLoaderUtils.getContextClassLoader();
             ClassLoader cl = new URLClassLoader(urls.toArray(new URL[0]), parentCl);
             if (className != null && !className.isEmpty()) {
                 return initTranslator(cl, className);
diff --git a/api/src/main/java/ai/djl/util/ClassLoaderUtils.java b/api/src/main/java/ai/djl/util/ClassLoaderUtils.java
index e45aaa266..4c51e2629 100644
--- a/api/src/main/java/ai/djl/util/ClassLoaderUtils.java
+++ b/api/src/main/java/ai/djl/util/ClassLoaderUtils.java
@@ -68,7 +68,7 @@ public final class ClassLoaderUtils {
                 urls[index++] = p.toUri().toURL();
             }
 
-            final ClassLoader contextCl = Thread.currentThread().getContextClassLoader();
+            final ClassLoader contextCl = getContextClassLoader();
             ClassLoader cl =
                     AccessController.doPrivileged(
                             (PrivilegedAction<ClassLoader>)
@@ -154,4 +154,17 @@ public final class ClassLoaderUtils {
         }
         return null;
     }
+
+    /**
+     * Returns the context class loader if available.
+     *
+     * @return the context class loader if available
+     */
+    public static ClassLoader getContextClassLoader() {
+        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        if (cl == null) {
+            return ClassLoaderUtils.class.getClassLoader(); // NOPMD
+        }
+        return cl;
+    }
 }
diff --git a/api/src/main/java/ai/djl/util/Platform.java b/api/src/main/java/ai/djl/util/Platform.java
index 363a0ab42..130021a69 100644
--- a/api/src/main/java/ai/djl/util/Platform.java
+++ b/api/src/main/java/ai/djl/util/Platform.java
@@ -59,7 +59,7 @@ public final class Platform {
         String nativeProp = "native/lib/" + engine + ".properties";
         Enumeration<URL> urls;
         try {
-            urls = Thread.currentThread().getContextClassLoader().getResources(nativeProp);
+            urls = ClassLoaderUtils.getContextClassLoader().getResources(nativeProp);
         } catch (IOException e) {
             throw new AssertionError("Failed to list property files.", e);
         }
diff --git a/basicdataset/src/main/java/ai/djl/basicdataset/cv/classification/ImageNet.java b/basicdataset/src/main/java/ai/djl/basicdataset/cv/classification/ImageNet.java
index ef828033a..2179fee06 100644
--- a/basicdataset/src/main/java/ai/djl/basicdataset/cv/classification/ImageNet.java
+++ b/basicdataset/src/main/java/ai/djl/basicdataset/cv/classification/ImageNet.java
@@ -15,6 +15,7 @@ package ai.djl.basicdataset.cv.classification;
 import ai.djl.modality.cv.transform.ToTensor;
 import ai.djl.training.dataset.Dataset;
 import ai.djl.translate.Pipeline;
+import ai.djl.util.ClassLoaderUtils;
 import ai.djl.util.JsonUtils;
 import ai.djl.util.Progress;
 import java.io.IOException;
@@ -101,7 +102,7 @@ public class ImageNet extends AbstractImageFolder {
     }
 
     private void loadSynset() {
-        ClassLoader cl = Thread.currentThread().getContextClassLoader();
+        ClassLoader cl = ClassLoaderUtils.getContextClassLoader();
         try (InputStream classStream = cl.getResourceAsStream("imagenet/classes.json")) {
             if (classStream == null) {
                 throw new AssertionError("Missing imagenet/classes.json in jar resource");


Test patch:
diff --git a/testing/src/main/java/ai/djl/testing/CoverageUtils.java b/testing/src/main/java/ai/djl/testing/CoverageUtils.java
index 77da7c378..1ae9c6256 100644
--- a/testing/src/main/java/ai/djl/testing/CoverageUtils.java
+++ b/testing/src/main/java/ai/djl/testing/CoverageUtils.java
@@ -12,6 +12,7 @@
  */
 package ai.djl.testing;
 
+import ai.djl.util.ClassLoaderUtils;
 import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Constructor;
@@ -98,13 +99,13 @@ public final class CoverageUtils {
 
     private static List<Class<?>> getClasses(Class<?> clazz)
             throws IOException, ReflectiveOperationException, URISyntaxException {
-        ClassLoader appClassLoader = Thread.currentThread().getContextClassLoader();
+        ClassLoader appClassLoader = ClassLoaderUtils.getContextClassLoader();
         Field field = appClassLoader.getClass().getDeclaredField("ucp");
         field.setAccessible(true);
         Object ucp = field.get(appClassLoader);
         Method method = ucp.getClass().getDeclaredMethod("getURLs");
         URL[] urls = (URL[]) method.invoke(ucp);
-        ClassLoader cl = new TestClassLoader(urls, Thread.currentThread().getContextClassLoader());
+        ClassLoader cl = new TestClassLoader(urls, ClassLoaderUtils.getContextClassLoader());
 
         URL url = clazz.getProtectionDomain().getCodeSource().getLocation();
         String path = url.getPath();