Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create OSGi bundles for ND4J #8083

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions nd4j/nd4j-backends/nd4j-api-parent/nd4j-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@
<artifactId>commons-math3</artifactId>
<version>${commons-math3.version}</version>
</dependency>

<dependency>
<groupId>org.osgi</groupId>
<artifactId>osgi.core</artifactId>
</dependency>
<!-- Tensorflow import -->

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<NDArrayCompressor> discoveredCompressors = serviceLoaderDiscovery(NDArrayCompressor.class);


ServiceLoader<NDArrayCompressor> loader = ServiceLoader.load(NDArrayCompressor.class);
for (NDArrayCompressor compressor : loader) {
for (NDArrayCompressor compressor : discoveredCompressors) {
codecs.put(compressor.getDescriptor().toUpperCase(), compressor);
}

Expand All @@ -65,6 +81,45 @@ protected void loadCompressors() {
}
}

private static <T> Iterable<T> serviceLoaderDiscovery(Class<T> iface) {

// Always use the default service loader behaviour (TCCL)
Iterable<T> 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<BundleWire> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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);
Expand Down Expand Up @@ -5590,6 +5594,17 @@ public void initWithBackend(Nd4jBackend backend) {

}

private <T> 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")
Expand Down
53 changes: 53 additions & 0 deletions nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native-osgi/bnd.bnd
Original file line number Diff line number Diff line change
@@ -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}"
95 changes: 95 additions & 0 deletions nd4j/nd4j-backends/nd4j-backend-impls/nd4j-native-osgi/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nd4j-backend-impls</artifactId>
<groupId>org.nd4j</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>nd4j-native-osgi</artifactId>
<name>nd4j-native-osgi</name>
<description>An OSGi packaging of the ND4J native backend implementation</description>


<dependencies>
<dependency>
<groupId>org.nd4j</groupId>
<artifactId>nd4j-osgi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.nd4j</groupId>
<artifactId>nd4j-native</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.nd4j</groupId>
<artifactId>nd4j-native</artifactId>
<version>${project.version}</version>
<classifier>${javacpp.platform}</classifier>
</dependency>
</dependencies>

<build>
<plugins>
<!-- Use the bnd-maven-plugin -->
<plugin>
<groupId>biz.aQute.bnd</groupId>
<artifactId>bnd-maven-plugin</artifactId>
<version>4.2.0</version>
<executions>
<execution>
<goals>
<goal>bnd-process</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Configure the jar to use the generated manifest -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>default-jar</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<excludes>
<exclude>lib/**</exclude>
</excludes>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
<classifier>${javacpp.platform}${javacpp.platform.extension}</classifier>
</configuration>
</execution>
<execution>
<id>${javacpp.platform}${javacpp.platform.extension}</id>
<configuration>
<skip>true</skip>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>