diff --git a/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/pom.xml b/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/pom.xml
index 7168b57e0852..dcb856407d4a 100644
--- a/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/pom.xml
+++ b/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/pom.xml
@@ -140,6 +140,11 @@
commons-math3
${commons-math3.version}
+
+
+ org.osgi
+ osgi.core
+
diff --git a/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/nd4j/linalg/compression/BasicNDArrayCompressor.java b/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/nd4j/linalg/compression/BasicNDArrayCompressor.java
index 665d6ce19cf8..c6da198a032f 100644
--- a/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/nd4j/linalg/compression/BasicNDArrayCompressor.java
+++ b/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/nd4j/linalg/compression/BasicNDArrayCompressor.java
@@ -23,7 +23,20 @@
import org.nd4j.linalg.api.buffer.DataType;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.factory.Nd4j;
+import org.nd4j.linalg.factory.Nd4jBackend;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+
+import static org.osgi.framework.namespace.PackageNamespace.PACKAGE_NAMESPACE;
+
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
@@ -49,9 +62,12 @@ protected void loadCompressors() {
We scan classpath for NDArrayCompressor implementations and add them one by one to codecs map
*/
codecs = new ConcurrentHashMap<>();
+
+
+ Iterable discoveredCompressors = serviceLoaderDiscovery(NDArrayCompressor.class);
+
- ServiceLoader loader = ServiceLoader.load(NDArrayCompressor.class);
- for (NDArrayCompressor compressor : loader) {
+ for (NDArrayCompressor compressor : discoveredCompressors) {
codecs.put(compressor.getDescriptor().toUpperCase(), compressor);
}
@@ -65,6 +81,45 @@ protected void loadCompressors() {
}
}
+ private static Iterable serviceLoaderDiscovery(Class iface) {
+
+ // Always use the default service loader behaviour (TCCL)
+ Iterable services = ServiceLoader.load(iface);
+
+ try {
+ Bundle bundle = FrameworkUtil.getBundle(iface);
+
+ if(bundle != null) {
+ // This ND4J is running in an OSGi framework, make sure we search
+ // 1. The API bundle (to pick up any default handlers)
+ // 2. Any other bundles that import the service API package
+
+ services = Iterables.concat(services, ServiceLoader.load(iface, iface.getClassLoader()));
+
+ BundleWiring wiring = bundle.adapt(BundleWiring.class);
+ List packageConsumers = wiring.getProvidedWires(PACKAGE_NAMESPACE);
+
+ if(packageConsumers != null) {
+ for(BundleWire wire : packageConsumers) {
+ if(!NDArrayCompressor.class.getPackage().getName().equals(
+ wire.getCapability().getAttributes().get(PACKAGE_NAMESPACE))) {
+ continue;
+ }
+
+ ClassLoader providerClassLoader = wire.getRequirerWiring().getClassLoader();
+ services = Iterables.concat(services,
+ ServiceLoader.load(iface, providerClassLoader));
+ }
+ }
+
+ }
+
+ } catch (NoClassDefFoundError e) {
+ // We're not running in OSGi
+ }
+ return services;
+ }
+
/**
* Get the set of available codecs for
* compression
diff --git a/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/nd4j/linalg/factory/Nd4j.java b/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/nd4j/linalg/factory/Nd4j.java
index b82ce008ac80..d7a7900ea7cd 100644
--- a/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/nd4j/linalg/factory/Nd4j.java
+++ b/nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/nd4j/linalg/factory/Nd4j.java
@@ -5492,46 +5492,50 @@ public void initWithBackend(Nd4jBackend backend) {
DataTypeUtil.setDTypeForContext(dtype);
}
+ ClassLoader backendLoader = backend.getClass().getClassLoader();
+
compressDebug = pp.toBoolean(COMPRESSION_DEBUG);
char ORDER = pp.toChar(ORDER_KEY, NDArrayFactory.C);
+
+ // These types have no default and must be loaded from the plugin classloader
Class extends BasicAffinityManager> affinityManagerClazz = (Class extends BasicAffinityManager>) Class
- .forName(pp.toString(AFFINITY_MANAGER));
+ .forName(pp.toString(AFFINITY_MANAGER), true, backendLoader);
affinityManager = affinityManagerClazz.newInstance();
Class extends NDArrayFactory> ndArrayFactoryClazz = (Class extends NDArrayFactory>) Class.forName(
- pp.toString(NDARRAY_FACTORY_CLASS));
+ pp.toString(NDARRAY_FACTORY_CLASS), true, backendLoader);
Class extends NDArrayFactory> sparseNDArrayClazz = (Class extends NDArrayFactory>) Class.forName(
- pp.toString(SPARSE_NDARRAY_FACTORY_CLASS));
- Class extends ConvolutionInstance> convolutionInstanceClazz = (Class extends ConvolutionInstance>) Class
- .forName(pp.toString(CONVOLUTION_OPS, DefaultConvolutionInstance.class.getName()));
- String defaultName = pp.toString(DATA_BUFFER_OPS, DefaultDataBufferFactory.class.getName());
- Class extends DataBufferFactory> dataBufferFactoryClazz = (Class extends DataBufferFactory>) Class
- .forName(pp.toString(DATA_BUFFER_OPS, defaultName));
+ pp.toString(SPARSE_NDARRAY_FACTORY_CLASS), true, backendLoader);
Class extends BaseShapeInfoProvider> shapeInfoProviderClazz = (Class extends BaseShapeInfoProvider>) Class
- .forName(pp.toString(SHAPEINFO_PROVIDER));
+ .forName(pp.toString(SHAPEINFO_PROVIDER), true, backendLoader);
Class extends BaseSparseInfoProvider> sparseInfoProviderClazz = (Class extends BaseSparseInfoProvider>) Class.forName(
- pp.toString(SPARSEINFO_PROVIDER));
-
+ pp.toString(SPARSEINFO_PROVIDER), true, backendLoader);
Class extends BasicConstantHandler> constantProviderClazz = (Class extends BasicConstantHandler>) Class
- .forName(pp.toString(CONSTANT_PROVIDER));
-
+ .forName(pp.toString(CONSTANT_PROVIDER), true, backendLoader);
Class extends BasicMemoryManager> memoryManagerClazz = (Class extends BasicMemoryManager>) Class
- .forName(pp.toString(MEMORY_MANAGER));
+ .forName(pp.toString(MEMORY_MANAGER), true, backendLoader);
+ Class extends MemoryWorkspaceManager> workspaceManagerClazz = (Class extends MemoryWorkspaceManager>) Class
+ .forName(pp.toString(WORKSPACE_MANAGER), true, backendLoader);
+ Class extends BlasWrapper> blasWrapperClazz = (Class extends BlasWrapper>) Class
+ .forName(pp.toString(BLAS_OPS), true, backendLoader);
+ Class extends BlasWrapper> sparseBlasWrapperClazz = (Class extends BlasWrapper>) Class
+ .forName(pp.toString(SPARSE_BLAS_OPS), true, backendLoader);
+
+ // These types have defaults and so we use the getPluginClass method
+ Class extends ConvolutionInstance> convolutionInstanceClazz = getPluginClass(pp, CONVOLUTION_OPS,
+ DefaultConvolutionInstance.class, backendLoader);
+ Class extends DataBufferFactory> dataBufferFactoryClazz = getPluginClass(pp, DATA_BUFFER_OPS,
+ DefaultDataBufferFactory.class, backendLoader);
+
allowsOrder = backend.allowsOrder();
- String rand = pp.toString(RANDOM_PROVIDER, DefaultRandom.class.getName());
- Class extends org.nd4j.linalg.api.rng.Random> randomClazz = (Class extends org.nd4j.linalg.api.rng.Random>) Class.forName(rand);
+ Class extends org.nd4j.linalg.api.rng.Random> randomClazz = getPluginClass(pp, RANDOM_PROVIDER,
+ DefaultRandom.class, backendLoader);
randomFactory = new RandomFactory(randomClazz);
- Class extends MemoryWorkspaceManager> workspaceManagerClazz = (Class extends MemoryWorkspaceManager>) Class
- .forName(pp.toString(WORKSPACE_MANAGER));
- Class extends BlasWrapper> blasWrapperClazz = (Class extends BlasWrapper>) Class
- .forName(pp.toString(BLAS_OPS));
- Class extends BlasWrapper> sparseBlasWrapperClazz = (Class extends BlasWrapper>) Class
- .forName(pp.toString(SPARSE_BLAS_OPS));
- String clazzName = pp.toString(DISTRIBUTION, DefaultDistributionFactory.class.getName());
- Class extends DistributionFactory> distributionFactoryClazz = (Class extends DistributionFactory>) Class.forName(clazzName);
+ Class extends DistributionFactory> distributionFactoryClazz = getPluginClass(pp, DISTRIBUTION,
+ DefaultDistributionFactory.class, backendLoader);
memoryManager = memoryManagerClazz.newInstance();
@@ -5540,8 +5544,8 @@ public void initWithBackend(Nd4jBackend backend) {
sparseInfoProvider = sparseInfoProviderClazz.newInstance();
workspaceManager = workspaceManagerClazz.newInstance();
- Class extends OpExecutioner> opExecutionerClazz = (Class extends OpExecutioner>) Class
- .forName(pp.toString(OP_EXECUTIONER, DefaultOpExecutioner.class.getName()));
+ Class extends OpExecutioner> opExecutionerClazz = getPluginClass(pp, OP_EXECUTIONER,
+ DefaultOpExecutioner.class, backendLoader);
OP_EXECUTIONER_INSTANCE = opExecutionerClazz.newInstance();
Constructor c2 = ndArrayFactoryClazz.getConstructor(DataType.class, char.class);
@@ -5590,6 +5594,17 @@ public void initWithBackend(Nd4jBackend backend) {
}
+ private Class extends T> getPluginClass(PropertyParser pp, String key,
+ Class extends T> defaultClass, ClassLoader pluginClassLoader) throws ClassNotFoundException {
+ String className = pp.getProperties().getProperty(key);
+
+ if(className == null) {
+ return defaultClass;
+ } else {
+ return (Class extends T>) Class.forName(className, true, pluginClassLoader);
+ }
+ }
+
private static boolean isSupportedPlatform() {
return (System.getProperty("java.vm.name").equalsIgnoreCase("Dalvik")
|| System.getProperty("os.arch").toLowerCase().startsWith("arm")
diff --git a/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native-osgi/bnd.bnd b/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native-osgi/bnd.bnd
new file mode 100644
index 000000000000..7224f0c491c3
--- /dev/null
+++ b/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native-osgi/bnd.bnd
@@ -0,0 +1,53 @@
+# This file configures the Manifest for the native backend bundle
+
+Bundle-SymbolicName: org.nd4j.backend.native
+
+# A safe version to use in dependency matching
+safe.version: ${maven_version;${project.version}}
+
+Bundle-Version: ${safe.version}
+
+# Java CPP requires that the JNI classes and the Java CPP library are loaded from
+# the same classloader. JavaCPP is packaged in with the frontend, so we attach as
+# a fragment to ensure we have a single flat class space. Ideally JavaCPP wouldn't
+# require this and then the backend could be a totally separate module.
+
+Fragment-Host: org.nd4j.frontend;bundle-version="[${safe.version},${safe.version}]"
+
+# These packages are needed by the generated code
+-conditionalpackage: \
+ org.bytedeco.mkl.*,\
+ org.bytedeco.openblas.*
+
+# We slurp in the java code, and the native libraries needed by openblas and mkl
+-includeresource: \
+ @nd4j-native-api-${project.version}.jar,\
+ @nd4j-native-${project.version}.jar,\
+ @nd4j-native-${project.version}-${javacpp.platform}${javacpp.platform.extension}.jar,\
+ @openblas-${openblas.version}-${javacpp-presets.version}-${dependency.platform}.jar!/!META-INF/versions/*,\
+ @mkl-${mkl.version}-${javacpp-presets.version}-${dependency.platform2}.jar!/!META-INF/versions/*,\
+ @mkl-dnn-${mkl-dnn.javacpp.version}-${dependency.platform2}.jar!/!META-INF/versions/*
+
+
+# Set the native code location and requirements
+Bundle-NativeCode: \
+ org/nd4j/nativeblas/${javacpp.platform}${javacpp.platform.extension}/libnd4jcpu.${os.lib.extension};\
+ org/nd4j/nativeblas/${javacpp.platform}${javacpp.platform.extension}/libjnind4jcpu.${os.lib.extension};\
+ org/bytedeco/openblas/${dependency.platform}/libjniopenblas_nolapack.${os.lib.extension};\
+ org/bytedeco/openblas/${dependency.platform}/libjniopenblas.${os.lib.extension};\
+ org/bytedeco/openblas/${dependency.platform}/libopenblas.${os.lib.extension};\
+ org/bytedeco/mkl/${dependency.platform2}/libjnimkl_rt.${os.lib.extension};\
+ org/bytedeco/mkldnn/${dependency.platform2}/libgcc_s.1.${os.lib.extension};\
+ org/bytedeco/mkldnn/${dependency.platform2}/libgomp.1.${os.lib.extension};\
+ org/bytedeco/mkldnn/${dependency.platform2}/libiomp5.${os.lib.extension};\
+ org/bytedeco/mkldnn/${dependency.platform2}/libjnimkldnn.${os.lib.extension};\
+ org/bytedeco/mkldnn/${dependency.platform2}/libjnimklml.${os.lib.extension};\
+ org/bytedeco/mkldnn/${dependency.platform2}/libmkldnn.0.${os.lib.extension};\
+ org/bytedeco/mkldnn/${dependency.platform2}/libmklml.${os.lib.extension};\
+ org/bytedeco/mkldnn/${dependency.platform2}/libstdc++.6.${os.lib.extension};\
+ osname=${os.name};\
+ processor=${os.arch}
+
+
+# Advertise that we provide a backend for ND4J
+Provide-Capability: org.nd4j.backend; backend.type="cpu"; version:Version="${safe.version}"
\ No newline at end of file
diff --git a/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native-osgi/pom.xml b/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native-osgi/pom.xml
new file mode 100644
index 000000000000..0a2b13b711d7
--- /dev/null
+++ b/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native-osgi/pom.xml
@@ -0,0 +1,95 @@
+
+
+
+
+ nd4j-backend-impls
+ org.nd4j
+ 1.0.0-SNAPSHOT
+
+ 4.0.0
+
+ nd4j-native-osgi
+ nd4j-native-osgi
+ An OSGi packaging of the ND4J native backend implementation
+
+
+
+
+ org.nd4j
+ nd4j-osgi
+ ${project.version}
+
+
+ org.nd4j
+ nd4j-native
+ ${project.version}
+
+
+ org.nd4j
+ nd4j-native
+ ${project.version}
+ ${javacpp.platform}
+
+
+
+
+
+
+
+ biz.aQute.bnd
+ bnd-maven-plugin
+ 4.2.0
+
+
+
+ bnd-process
+
+
+
+
+
+
+ maven-jar-plugin
+
+
+ default-jar
+ package
+
+ jar
+
+
+
+ lib/**
+
+
+ ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
+ ${javacpp.platform}${javacpp.platform.extension}
+
+
+
+ ${javacpp.platform}${javacpp.platform.extension}
+
+ true
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native-platform/pom.xml b/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native-platform/pom.xml
index ff85e63e7dbb..df1dd26c46e7 100644
--- a/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native-platform/pom.xml
+++ b/nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native-platform/pom.xml
@@ -27,6 +27,7 @@
nd4j-native
+ nd4j-native-osgi
@@ -116,6 +117,72 @@
${project.version}
${javacpp.platform.linux-armhf}
+
+ ${project.groupId}
+ ${nd4j.backend.osgi}
+ ${project.version}
+ ${javacpp.platform.android-arm}
+
+
+ ${project.groupId}
+ ${nd4j.backend.osgi}
+ ${project.version}
+ ${javacpp.platform.android-arm64}
+
+
+ ${project.groupId}
+ ${nd4j.backend.osgi}
+ ${project.version}
+ ${javacpp.platform.android-x86}
+
+
+ ${project.groupId}
+ ${nd4j.backend.osgi}
+ ${project.version}
+ ${javacpp.platform.android-x86_64}
+
+
+ ${project.groupId}
+ ${nd4j.backend.osgi}
+ ${project.version}
+ ${javacpp.platform.ios-arm64}
+
+
+ ${project.groupId}
+ ${nd4j.backend.osgi}
+ ${project.version}
+ ${javacpp.platform.ios-x86_64}
+
+
+ ${project.groupId}
+ ${nd4j.backend.osgi}
+ ${project.version}
+ ${javacpp.platform.linux-x86_64}
+
+
+ ${project.groupId}
+ ${nd4j.backend.osgi}
+ ${project.version}
+ ${javacpp.platform.macosx-x86_64}
+
+
+ ${project.groupId}
+ ${nd4j.backend.osgi}
+ ${project.version}
+ ${javacpp.platform.windows-x86_64}
+
+
+ ${project.groupId}
+ ${nd4j.backend.osgi}
+ ${project.version}
+ ${javacpp.platform.linux-ppc64le}
+
+
+ ${project.groupId}
+ ${nd4j.backend.osgi}
+ ${project.version}
+ ${javacpp.platform.linux-armhf}
+
diff --git a/nd4j/nd4j-backends/nd4j-backend-impls/pom.xml b/nd4j/nd4j-backends/nd4j-backend-impls/pom.xml
index 86ce07ff7a6b..1834ff84c0c8 100644
--- a/nd4j/nd4j-backends/nd4j-backend-impls/pom.xml
+++ b/nd4j/nd4j-backends/nd4j-backend-impls/pom.xml
@@ -184,6 +184,7 @@
nd4j-native
+ nd4j-native-osgi
nd4j-native-platform
diff --git a/nd4j/nd4j-backends/nd4j-tests-osgi/bnd.bnd b/nd4j/nd4j-backends/nd4j-tests-osgi/bnd.bnd
new file mode 100644
index 000000000000..66a2e2443c9d
--- /dev/null
+++ b/nd4j/nd4j-backends/nd4j-tests-osgi/bnd.bnd
@@ -0,0 +1 @@
+Test-Cases: ${classes;NAMED;*Test}
\ No newline at end of file
diff --git a/nd4j/nd4j-backends/nd4j-tests-osgi/cpu.bndrun b/nd4j/nd4j-backends/nd4j-tests-osgi/cpu.bndrun
new file mode 100644
index 000000000000..947ec09e1202
--- /dev/null
+++ b/nd4j/nd4j-backends/nd4j-tests-osgi/cpu.bndrun
@@ -0,0 +1,29 @@
+-standalone: target/index.xml
+-runfw: org.apache.felix.framework;version='[6.0.3,6.0.3]'
+-runsystemcapabilities: ${native_capability}
+-runee: JavaSE-1.8
+-runrequires: bnd.identity;id='nd4j-tests-osgi'
+-runbundles: \
+ ch.qos.logback.classic;version='[1.2.3,1.2.4)',\
+ ch.qos.logback.core;version='[1.2.3,1.2.4)',\
+ com.google.flatbuffers.java;version='[1.10.0,1.10.1)',\
+ com.google.guava;version='[20.0.0,20.0.1)',\
+ com.sun.jna;version='[4.3.0,4.3.1)',\
+ com.sun.jna.platform;version='[4.3.0,4.3.1)',\
+ jackson;version='[1.0.0,1.0.1)',\
+ joda-time;version='[2.2.0,2.2.1)',\
+ nd4j-tests-osgi;version='[1.0.0,1.0.1)',\
+ org.apache.commons.codec;version='[1.10.0,1.10.1)',\
+ org.apache.commons.compress;version='[1.16.1,1.16.2)',\
+ org.apache.commons.io;version='[2.5.0,2.5.1)',\
+ org.apache.commons.lang3;version='[3.6.0,3.6.1)',\
+ org.apache.commons.math3;version='[3.5.0,3.5.1)',\
+ org.apache.commons.net;version='[3.1.0,3.1.1)',\
+ org.apache.servicemix.bundles.junit;version='[4.12.0,4.12.1)',\
+ org.nd4j.backend.native;version='[1.0.0,1.0.1)',\
+ org.nd4j.frontend;version='[1.0.0,1.0.1)',\
+ org.objenesis;version='[2.6.0,2.6.1)',\
+ org.threeten.bp;version='[1.3.3,1.3.4)',\
+ slf4j.api;version='[1.7.21,1.7.22)',\
+ stax2-api;version='[3.1.4,3.1.5)',\
+ uk.com.robust-it.cloning;version='[1.9.3,1.9.4)'
\ No newline at end of file
diff --git a/nd4j/nd4j-backends/nd4j-tests-osgi/pom.xml b/nd4j/nd4j-backends/nd4j-tests-osgi/pom.xml
new file mode 100644
index 000000000000..15e5b273e121
--- /dev/null
+++ b/nd4j/nd4j-backends/nd4j-tests-osgi/pom.xml
@@ -0,0 +1,140 @@
+
+
+
+
+ nd4j-backends
+ org.nd4j
+ 1.0.0-SNAPSHOT
+
+ 4.0.0
+
+ nd4j-tests-osgi
+ jar
+
+ nd4j-tests-osgi
+
+
+ 4.2.0
+
+
+
+
+ org.nd4j
+ nd4j-osgi
+ ${project.version}
+
+
+ org.nd4j
+ nd4j-native-osgi
+ ${project.version}
+ ${javacpp.platform}${javacpp.platform.extension}
+
+
+
+ org.apache.servicemix.bundles
+ org.apache.servicemix.bundles.junit
+ 4.12_1
+
+
+
+ ch.qos.logback
+ logback-classic
+ ${logback.version}
+ runtime
+
+
+
+ ch.qos.logback
+ logback-core
+ ${logback.version}
+ runtime
+
+
+
+ org.apache.felix
+ org.apache.felix.framework
+ 6.0.3
+ runtime
+
+
+
+
+
+
+
+ biz.aQute.bnd
+ bnd-maven-plugin
+ ${bnd.version}
+
+
+
+ bnd-process
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.0.2
+
+
+ ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
+ true
+
+
+
+ biz.aQute.bnd
+ bnd-indexer-maven-plugin
+ ${bnd.version}
+
+ false
+ REQUIRED
+ true
+
+
+
+
+ index
+
+
+
+
+
+ biz.aQute.bnd
+ bnd-testing-maven-plugin
+ ${bnd.version}
+
+ false
+
+ cpu.bndrun
+
+
+
+
+
+ testing
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/nd4j/nd4j-backends/nd4j-tests-osgi/src/main/java/org/nd4j/test/osgi/NDArrayCreationTest.java b/nd4j/nd4j-backends/nd4j-tests-osgi/src/main/java/org/nd4j/test/osgi/NDArrayCreationTest.java
new file mode 100644
index 000000000000..8db0fdac2065
--- /dev/null
+++ b/nd4j/nd4j-backends/nd4j-tests-osgi/src/main/java/org/nd4j/test/osgi/NDArrayCreationTest.java
@@ -0,0 +1,88 @@
+package org.nd4j.test.osgi;
+/*******************************************************************************
+ * Copyright (c) 2015-2018 Skymind, Inc.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Apache License, Version 2.0 which is available at
+ * https://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ ******************************************************************************/
+
+
+import static org.junit.Assert.assertEquals;
+
+import org.bytedeco.javacpp.FloatPointer;
+import org.bytedeco.javacpp.Pointer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.nd4j.linalg.api.buffer.DataBuffer;
+import org.nd4j.linalg.api.buffer.DataType;
+import org.nd4j.linalg.api.memory.MemoryWorkspace;
+import org.nd4j.linalg.api.ndarray.INDArray;
+import org.nd4j.linalg.factory.Nd4j;
+import org.nd4j.linalg.factory.Nd4jBackend;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This test is based on the TestNDArrayCreation test from the nd4j-tests module
+ */
+public class NDArrayCreationTest {
+
+ private static Logger log = LoggerFactory.getLogger(NDArrayCreationTest.class);
+
+ @Before
+ public void before() throws Exception {
+ Nd4j nd4j = new Nd4j();
+ nd4j.initWithBackend(Nd4jBackend.load());
+ Nd4j.factory().setOrder('c');
+ Nd4j.getExecutioner().enableDebugMode(false);
+ Nd4j.getExecutioner().enableVerboseMode(false);
+ Nd4j.setDefaultDataTypes(DataType.DOUBLE, DataType.DOUBLE);
+ }
+
+ @After
+ public void after() throws Exception {
+ Nd4j.getMemoryManager().purgeCaches();
+
+ //Attempt to keep workspaces isolated between tests
+ Nd4j.getWorkspaceManager().destroyAllWorkspacesForCurrentThread();
+ MemoryWorkspace currWS = Nd4j.getMemoryManager().getCurrentWorkspace();
+ Nd4j.getMemoryManager().setCurrentWorkspace(null);
+ if(currWS != null){
+ //Not really safe to continue testing under this situation... other tests will likely fail with obscure
+ // errors that are hard to track back to this
+ log.error("Open workspace leaked from test! Exiting - {}, isOpen = {} - {}", currWS.getId(), currWS.isScopeActive(), currWS);
+ System.exit(1);
+ }
+ }
+
+ @Test
+ // We only run with CPU for now
+ //@Ignore("AB 2019/05/23 - Failing on linux-x86_64-cuda-9.2 - see issue #7657")
+ public void testBufferCreation() {
+ DataBuffer dataBuffer = Nd4j.createBuffer(new float[] {1, 2});
+ Pointer pointer = dataBuffer.pointer();
+ FloatPointer floatPointer = new FloatPointer(pointer);
+ DataBuffer dataBuffer1 = Nd4j.createBuffer(floatPointer, 2, DataType.FLOAT);
+
+ assertEquals(2, dataBuffer.length());
+ assertEquals(1.0, dataBuffer.getDouble(0), 1e-1);
+ assertEquals(2.0, dataBuffer.getDouble(1), 1e-1);
+
+ assertEquals(2, dataBuffer1.length());
+ assertEquals(1.0, dataBuffer1.getDouble(0), 1e-1);
+ assertEquals(2.0, dataBuffer1.getDouble(1), 1e-1);
+ INDArray arr = Nd4j.create(dataBuffer1);
+ System.out.println(arr);
+ }
+
+}
diff --git a/nd4j/nd4j-backends/pom.xml b/nd4j/nd4j-backends/pom.xml
index 59228e1419f3..3f90cd77a2d6 100644
--- a/nd4j/nd4j-backends/pom.xml
+++ b/nd4j/nd4j-backends/pom.xml
@@ -33,6 +33,7 @@
nd4j-backend-impls
nd4j-api-parent
nd4j-tests-tensorflow
+ nd4j-tests-osgi
diff --git a/nd4j/nd4j-context/pom.xml b/nd4j/nd4j-context/pom.xml
index 225b3784c504..896ce0d9ce99 100644
--- a/nd4j/nd4j-context/pom.xml
+++ b/nd4j/nd4j-context/pom.xml
@@ -33,6 +33,11 @@
nd4j-common
${project.version}
+
+
+ org.osgi
+ osgi.core
+
diff --git a/nd4j/nd4j-context/src/main/java/org/nd4j/linalg/factory/Nd4jBackend.java b/nd4j/nd4j-context/src/main/java/org/nd4j/linalg/factory/Nd4jBackend.java
index bcdfba202b55..774735b97efc 100644
--- a/nd4j/nd4j-context/src/main/java/org/nd4j/linalg/factory/Nd4jBackend.java
+++ b/nd4j/nd4j-context/src/main/java/org/nd4j/linalg/factory/Nd4jBackend.java
@@ -21,9 +21,15 @@
import org.nd4j.config.ND4JSystemProperties;
import org.nd4j.context.Nd4jContext;
import org.nd4j.linalg.io.Resource;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.collect.Iterators;
+
import java.io.File;
import java.io.IOException;
import java.security.PrivilegedActionException;
@@ -153,10 +159,8 @@ public abstract class Nd4jBackend {
public static Nd4jBackend load() throws NoAvailableBackendException {
List backends = new ArrayList<>(1);
- ServiceLoader loader = ServiceLoader.load(Nd4jBackend.class);
+ Iterator backendIterator = getServices();
try {
-
- Iterator backendIterator = loader.iterator();
while (backendIterator.hasNext())
backends.add(backendIterator.next());
@@ -222,6 +226,35 @@ public int compare(Nd4jBackend o1, Nd4jBackend o2) {
return load();
}
+ private static Iterator getServices() {
+
+ Iterator discoveredBackends = ServiceLoader.load(Nd4jBackend.class).iterator();
+
+ try {
+ Bundle bundle = FrameworkUtil.getBundle(Nd4jBackend.class);
+
+
+ if(bundle != null) {
+ BundleWiring wiring = bundle.adapt(BundleWiring.class);
+ List requiredWires = wiring.getRequiredWires("org.nd4j.backend");
+
+ if(requiredWires != null) {
+ for(BundleWire wire : requiredWires) {
+ ClassLoader providerClassLoader = wire.getProviderWiring().getClassLoader();
+ discoveredBackends = Iterators.concat(discoveredBackends,
+ ServiceLoader.load(Nd4jBackend.class, providerClassLoader).iterator());
+ }
+ }
+
+ }
+
+ } catch (NoClassDefFoundError e) {
+ // We're not running in OSGi
+ }
+
+ return discoveredBackends;
+ }
+
/**
* Adds the supplied Java Archive library to java.class.path. This is benign
diff --git a/nd4j/nd4j-osgi/bnd.bnd b/nd4j/nd4j-osgi/bnd.bnd
new file mode 100644
index 000000000000..bf64ef6dabc3
--- /dev/null
+++ b/nd4j/nd4j-osgi/bnd.bnd
@@ -0,0 +1,74 @@
+# This file is used to configure the bnd-maven-plugin and to generate OSGi
+# metadata for the "frontend" bundle
+
+Bundle-SymbolicName: org.nd4j.frontend
+
+# A safe version to use in dependency matching
+safe.version: ${maven_version;${project.version}}
+
+Bundle-Version: ${safe.version}
+
+# Export all the packages from ND4J, except the ones with "impl" in the package name
+api.package.names: ${filterout;${packages;NAMED;org.nd4j.*|onnx|org.tensorflow.*|tensorflow*};impl}
+
+# For now, all the API packages use the same version, taken from the maven version.
+# Ideally the package versions would be maintained individually using annotations
+api.version: ${safe.version}
+
+# Assemble the full export string containing all the packages, versions, and directives
+# Note that the suffix prevents the API packages from being substitutably imported
+api.export.string: ${replace;${api.package.names};$;\\;version=${api.version}\\;-noimport:=true}
+
+
+# We export all of the ND4J API, plus any third party API that we contain which is exposed
+# through the ND4J API. Note that exposing third party API through your API constitutes
+# tight coupling and is evidence of a leaky abstraction. It may be that too many packages
+# from ND4J are being considered API and that they should be kept private. This would allow
+# the third party package exports to be removed.
+#
+# JavaCPP is exposed in lots of places, NeoIterTools is exposed via
+# org.nd4j.linalg.indexing. The shaded Protobuf is also exposed through several parts of
+# the ND4J API.
+# Take care with these dependencies as there are no variables for some of the versions.
+
+-exportcontents: \
+ ${api.export.string},\
+ com.github.os72.protobuf351;version=0.9,\
+ org.bytedeco.javacpp;version=${javacpp.version},\
+ org.bytedeco.javacpp.annotation;version=${javacpp.version},\
+ org.bytedeco.javacpp.indexer;version=${javacpp.version},\
+ org.bytedeco.javacpp.tools;version=${javacpp.version},\
+ net.ericaro.neoitertools;version=1.0.0
+
+# Embed the frontend jar files produced by ND4J. These must be delivered together
+# due to split packages between the various jar files. We also embed JavaCPP here
+# because it contains critical non-class file resources in the properties package
+-includeresource: \
+ @nd4j-common-${project.version}.jar,\
+ @nd4j-context-${project.version}.jar,\
+ @nd4j-buffer-${project.version}.jar,\
+ @nd4j-api-${project.version}.jar,\
+ @nd4j-native-api-${project.version}.jar,\
+ @javacpp-${javacpp.version}.jar!/!META-INF/*
+
+ # These are non-OSGi packaged dependencies which we therefore merge
+ # into this OSGi bundle as private copies
+ -conditionalpackage: \
+ com.github.os72.protobuf351.*,\
+ com.jakewharton.byteunits.*,\
+ org.bytedeco.javacpp.*,\
+ net.ericaro.neoitertools.*,\
+ oshi.*
+
+
+ # These dependencies must be customised to remove them or make them
+ # optional. The code paths that use them are not exercised by ND4J
+ Import-Package: \
+ !android.content,\
+ !org.apache.maven.*,\
+ com.sun.management;resolution:=optional,\
+ sun.misc;resolution:=optional,\
+ *
+
+# The ND4J API always requires a backend implementation
+Require-Capability: org.nd4j.backend; filter:="(version=${api.version})"; cardinality:=multiple
\ No newline at end of file
diff --git a/nd4j/nd4j-osgi/pom.xml b/nd4j/nd4j-osgi/pom.xml
new file mode 100644
index 000000000000..9a2090e690b4
--- /dev/null
+++ b/nd4j/nd4j-osgi/pom.xml
@@ -0,0 +1,104 @@
+
+
+
+
+ nd4j
+ org.nd4j
+ 1.0.0-SNAPSHOT
+
+ 4.0.0
+
+ nd4j-osgi
+ jar
+
+ nd4j-osgi
+
+ This module takes the ND4J frontend and packages it as an OSGi bundle.
+ It is necessary to repackage in this way due to the prevalence of split
+ packages that exist between various ND4J modules, which prevent the
+ individual jar files being used as standalone OSGi bundles
+
+
+
+
+
+
+ biz.aQute.bnd
+ bnd-maven-plugin
+ ${bnd.version}
+
+
+
+ bnd-process
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.0.2
+
+
+ ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
+ true
+
+
+
+
+
+
+
+
+
+ org.nd4j
+ nd4j-common
+ ${project.version}
+
+
+ org.nd4j
+ nd4j-context
+ ${project.version}
+
+
+ org.nd4j
+ nd4j-buffer
+ ${project.version}
+
+
+ org.nd4j
+ nd4j-api
+ ${project.version}
+
+
+ org.nd4j
+ nd4j-native-api
+ ${project.version}
+
+
+
+
+ 1.7
+ 1.7
+ 4.2.0
+
+
+
+
diff --git a/nd4j/nd4j-shade/jackson/bnd.bnd b/nd4j/nd4j-shade/jackson/bnd.bnd
new file mode 100644
index 000000000000..fb316a85116c
--- /dev/null
+++ b/nd4j/nd4j-shade/jackson/bnd.bnd
@@ -0,0 +1,16 @@
+# This file configures the package exports and versions for the
+# shaded jackson implementation. Normally annotations would be
+# used, but there is no source code for a shaded jar.
+
+# We export everything which is a bad practice, but it's
+# unclear which packages are actually needed by nd4j.
+# In future this can be improved by making a more restrictive
+# list of exported backages
+Export-Package: org.nd4j.shade.jackson.*;version=${jackson.version}
+
+# Exclude the import for the jackson jaxb module which isn't
+# provided by ND4J and isn't used. This helps to keep the
+# module behaviour consistent and clean, as well as avoiding
+# an unversioned import (an error-prone bad practice)
+Import-Package: !org.nd4j.shade.jackson.module.jaxb,\
+ *
\ No newline at end of file
diff --git a/nd4j/nd4j-shade/jackson/pom.xml b/nd4j/nd4j-shade/jackson/pom.xml
index 1d53e1c41354..b08d66f90871 100644
--- a/nd4j/nd4j-shade/jackson/pom.xml
+++ b/nd4j/nd4j-shade/jackson/pom.xml
@@ -30,6 +30,7 @@
true
+ org.nd4j.shade.jackson
@@ -157,6 +158,11 @@
maven-jar-plugin
true
+
+
+ ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
@@ -212,6 +218,18 @@
+
+ biz.aQute.bnd
+ bnd-maven-plugin
+ 4.2.0
+
+
+
+ bnd-process
+
+
+
+
diff --git a/nd4j/pom.xml b/nd4j/pom.xml
index 255859171c26..3d455db7892b 100644
--- a/nd4j/pom.xml
+++ b/nd4j/pom.xml
@@ -61,6 +61,7 @@
nd4j-backends
nd4j-parameter-server-parent
nd4j-uberjar
+ nd4j-osgi
nd4j-tensorflow
@@ -87,6 +88,12 @@
${junit.version}
test
+
+ org.osgi
+ osgi.core
+ 6.0.0
+ provided
+
diff --git a/pom.xml b/pom.xml
index 8b474e1d1a9a..6ba0759c0b47 100644
--- a/pom.xml
+++ b/pom.xml
@@ -868,6 +868,7 @@
linux
+ so
@@ -879,6 +880,7 @@
macosx
+ dylib
@@ -890,6 +892,7 @@
windows
+ dll