Access Tensorflow from Tomcat on CentOS Linux - java

I have a Java demo working that uses Tensorflow for image classification. It runs okay on Windows, but now I want to run it as a web service from the Java Tomcat web server.
I have added all the Tensorflow jars to Tomcat's lib, but Tensorflow has a jni dependency. I'm not sure how to install and link this so Tensorflow can run on the CentOS Linux server.
I have read this, but I do not need to run python on the server, just access Tensorflow from Java.
Update: **Okay, to get this to work on Tomcat on Windows I do the following,
download libtensorflow.jar from,
https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-1.6.0.jar
and then the dll from,
https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow_jni-cpu-windows-x86_64-1.6.0.zip
(extract zip to get dll)
See, https://www.tensorflow.org/install/install_java
put the jar in my tomcat lib, and create a tomcat dll dir and put the dll in it
edit my setenv.bat and add the line,
SET CATALINA_OPTS=-Xmx4g -XX:PermSize=128m -XX:MaxPermSize=512m -Djava.library.path=D:\Engineering\apache-tomcat-7.0.50\dll
This works on Windows.
For Linux, CentOS 6, I do the same, but instead of the dll download the so files from,
https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow_jni-cpu-linux-x86_64-1.6.0.tar.gz
and edit my setenv.sh and add the lines,
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/tomcat8/so"
export JAVA_OPTS="-server -Xmx38g -Djava.library.path=/usr/local/tomcat8/so"
export CATALINA_OPTS="-Djava.library.path=/usr/local/tomcat8/so"
But none of these seem to work, I always get the error,
Cannot find TensorFlow native library for OS: linux, architecture:
x86_64. See
https://github.com/tensorflow/tensorflow/tree/master/tensorflow/java/README.md
for possible solutions (such as building the library from source).
Additional information on attempts to find the native library can be
obtained by adding org.tensorflow.NativeLibrary.DEBUG=1 to the system
properties of the JVM.
I found there is another deployment option by instead just adding the jar,
to lib, and it will magically find the correct so files.
https://mvnrepository.com/artifact/org.tensorflow/libtensorflow_jni
When I try this option it seems to find the so files, but I get this error,
/usr/local/tomcat8/temp/tensorflow_native_libraries-1522357321965-0/libtensorflow_jni.so: /lib64/libc.so.6: version `GLIBC_2.16' not found (required by /usr/local/tomcat8/temp/tensorflow_native_libraries-1522357321965-0/libtensorflow_jni.so)
Seems like Tensorflow only supports a very specific OS and version??
I found this,
Error while importing Tensorflow in python2.7 in Ubuntu 12.04. 'GLIBC_2.17 not found'
But have not tried any of the options yet. Does not look promising for a production system.
Looking at what GLIBC is, it is for GPU, but I don't have or need to use a GPU, just want to use the CPU, why is this library required??
** Update
So... I tried to build glibc 1.6.0 on Centos6 so that I could use it by following,
https://unix.stackexchange.com/questions/176489/how-to-update-glibc-to-2-14-in-centos-6-5
The steps worked, but it lead to this error trying to run Tensorflow, seems like it has a dependecy on another lib...
error while loading shared libraries: __vdso_time: invalid mode for dlopen()
At this point I'm ready to give up, and try installing Centos7, but this route will require we upgrade 12 production servers...

Well, I have very little knowledge of tensorflow in Java. However, I did do a little research and believe I came to a conclusion to solving your problem.
Of course, the solution you posted in Error while importing Tensorflow in python2.7 in Ubuntu 12.04. 'GLIBC_2.17 not found' actually mitigates the problem if you read the solution by #Igor.
To understand the issue better: The way tensorflow works in java is that your package makes calls to the python library which underneath it actually calls C code which is where the main core and power of the library lies. So you may think of the Java package as a wrapper for the Python tensorflow which is a wrapper for a C library.
Recall that the linux operating systems are built in C and almost always has Glibc preinstalled as system requirements as said here in the first few lines. That being said, the issue you face is that GLibc that tensorflow requires is latest version which is not the same version run by your operating system.
If you read the issue here installation problem (version 'GLIBC' 2.14 not found), you'll see there is a similar issue where the operating system running is Cent OS which is the same as yours. The only difference in that specific issue is that the person in question is using python instead of java, but the issue is the same.
Thus, you have several possible ways to tackle it.
Run your code on another linux based operating system where you Glibc is compatible with tensorflow (or can be easily updated)
Upgrade your system GLIBC globally. This is extremely painful if you are running this on a server and is well documented here. (from what i understood, you may simply get away with installing python to resolve this. Sorry, I didn't read the article completely).
Add a second GLIBC to your system (Risky)
Compile Glibc and Bazel from source. (Sounds like the most plausible explanation to me after the first option)
Compile tensorflow from source to work on your current glibc as suggested by #Igor in this post. I have no idea if this would work, since I am not sure which C functionalities tensorflow library may be calling.
I hope this response was at least of little help. Cheers!

This problem is not directly related to Java, it's all about old good C and native libraries linkage. What happened (in short) :
Tensorflow's Java library makes runtime calls to native library through JNI (Java Native Interface)
This native library (.so file inside .jar) was compiled under 'fresher' Linux distro than Centos6, probably under Ubuntu LTS, that's why it was linked to fresher version of glibc library.
There is no simple way to manually update glibc and keep system stable, so best will be upgrade to CentOS 7, which has required version of glibc on board:
https://rpmfind.net/linux/rpm2html/search.php?query=libc.so.6%28GLIBC_2.16%29%2864bit%29&submit=Search+...&system=centos&arch=

I just had a closer look.
Simple add a dependency to org.tensorflow:tensorflow:1.4.0-rc0 (or whatever version you prefer) to you favorite build tool.
This will introduce a dependency to org.tensorflow:libtensorflow_jni:1.4.0-rc0. This will include the following:
blafasel#localhost:~$ unzip -t .m2/repository/org/tensorflow/libtensorflow_jni/1.4.0-rc0/libtensorflow_jni-1.4.0-rc0.jar
Archive: .m2/repository/org/tensorflow/libtensorflow_jni/1.4.0-rc0/libtensorflow_jni-1.4.0-rc0.jar
testing: META-INF/ OK
testing: META-INF/MANIFEST.MF OK
testing: org/ OK
testing: org/tensorflow/ OK
testing: org/tensorflow/native/ OK
testing: org/tensorflow/native/darwin-x86_64/ OK
testing: org/tensorflow/native/linux-x86_64/ OK
testing: org/tensorflow/native/windows-x86_64/ OK
testing: org/tensorflow/native/darwin-x86_64/libtensorflow_framework.so OK
testing: org/tensorflow/native/darwin-x86_64/LICENSE OK
testing: org/tensorflow/native/darwin-x86_64/libtensorflow_jni.dylib OK
testing: org/tensorflow/native/linux-x86_64/libtensorflow_framework.so OK
testing: org/tensorflow/native/linux-x86_64/libtensorflow_jni.so OK
testing: org/tensorflow/native/linux-x86_64/LICENSE OK
testing: org/tensorflow/native/windows-x86_64/tensorflow_jni.dll OK
testing: org/tensorflow/native/windows-x86_64/LICENSE OK
testing: META-INF/maven/ OK
testing: META-INF/maven/org.tensorflow/ OK
testing: META-INF/maven/org.tensorflow/libtensorflow_jni/ OK
testing: META-INF/maven/org.tensorflow/libtensorflow_jni/pom.xml OK
testing: META-INF/maven/org.tensorflow/libtensorflow_jni/pom.properties OK
No errors detected in compressed data of .m2/repository/org/tensorflow/libtensorflow_jni/1.4.0-rc0/libtensorflow_jni-1.4.0-rc0.jar.
As you can see this already contains all needed binaries to get JNI working on all officially supported platforms. That contains any Linux on x86_64.
As long as you don't try to use it on a raspi or on 32-bit CentOS and as long as you use a suitable build tool you should be save.
The only risk lies in dependencies of these libraries on other system libs. A call to ldd on libtensorflow_framework.so shows:
blafasel#localhost:~$ ldd org/tensorflow/native/linux-x86_64/libtensorflow_framework.so
linux-vdso.so.1 => (0x00007ffffaa62000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f07c6494000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f07c6290000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f07c6073000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f07c5cf0000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f07c5ada000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f07c5710000)
/lib64/ld-linux-x86-64.so.2 (0x000056525c661000)
If you don't find these transitive dependencies on your system you probably should try an older version of tensorflow or a newer of CentOs.

DISCLAIMER
Please consider that this answer is lengthier because it answers to the initially posted question, as well as to the other presented problems when the question evolved as more information was supplied through comments and discussions.
UPDATE 2:
New information supplied suggests that the glibc version on the centOS6 server is older than the glibc version tensorflow binary was compiled against. To update the glibc version on the CentOS6 server to newer one, you can try the steps as described in this upgrade script (credit to origin).
I would recommend upgrading the whole server, instead of glibc only.
A lot of other commands on your current server are compiled against your current glibc version. If you do an upgrade of that library you might run into compatibility issues and this might result in the server being broken all together.
#! /bin/sh
# update glibc to 2.17 for CentOS 6
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-2.17-55.el6.x86_64.rpm
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-common-2.17-55.el6.x86_64.rpm
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-devel-2.17-55.el6.x86_64.rpm
wget http://copr-be.cloud.fedoraproject.org/results/mosquito/myrepo-el6/epel-6-x86_64/glibc-2.17-55.fc20/glibc-headers-2.17-55.el6.x86_64.rpm
sudo rpm -Uvh glibc-2.17-55.el6.x86_64.rpm \
glibc-common-2.17-55.el6.x86_64.rpm \
glibc-devel-2.17-55.el6.x86_64.rpm \
glibc-headers-2.17-55.el6.x86_64.rpm
Original Answer:
There's a jar file containing the JNI distribution of tensorflow.
You can simply download the tensorflow_jni.jar using the version that matches the tensorflow.jar - in your case 1.6.0 and package that alongside your application. The JNI jar will be on the class path and will be picked up automatically.
You can also just copy-paste the tensorflow_jni.jar in tomcat's lib folder.
The tensorflow_jni.jar is configured for CPU usage, if you want to use the GPU one, you can download tensorflow_jni_gpu.jar instead.
Demo:
I've made a demo application that is deployed as war package to a dedicated Tomcat 8.5.29, with a single rest endpoint that prints the tensorflow version and I can confirm that providing both tensorflow.jar and tensorflow_jni.jar works, without any additional configuration or tweaking.
I've uploaded the test application in my github account. You can check it out, package it as a war file (mvn package or whatever you are using to do that) and deploy it in Tomcat.
To package it as described, it will require maven, but the main purpose for maven in this case is to download the necessary dependencies declared in the pom file.
If you don't want to use maven, you can download the dependencies by hand, from the provided links above and incorporate them in your application setup.
UPDATE - configuring native libs in dedicated Tomcat
Here's how I did the setup with dedicated Tomcat8, where all the tensorflow dependencies are configured in the web server and not coming with the deployed application.
1) Here's how my war dependency looks like - it has 0 tensorflow dependencies:
In order to produce that with the linked project, you just have to mark the tensorflow dependency as provided in pom.xml:
<dependency>
<groupId>org.tensorflow</groupId>
<artifactId>tensorflow</artifactId>
<version>1.6.0</version>
<scope>provided</scope> <!-- add this line -->
</dependency>
And pick up the demo-tensorflow-0.0.1-SNAPSHOT.war.original war from target directory (remove .original before deploying to tomcat).
2) Here is the path to the SO files on the file system, reflecting the path you have specified:
3) Tomcat's lib folder:
4) If I deploy the war package in tomcat and try to access the rest endpoint I will get the same error you're getting:
5) I've created setenv.sh in CATALINA_BASE (I've added the library path only in CATALINA_OPTS for clarity).
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/tomcat8/so"
export JAVA_OPTS="-server -Xmx38g"
export CATALINA_OPTS="-Djava.library.path=/usr/local/tomcat8/so"
and then
chmod u+x setenv.sh
6) Running tomcat I can see in the log messages the configuration is picked up:
7) Accessing the application this time is successful:

Related

java spring boot mircoservices wont start on one pc but it will on others

I'm working on a java program made using microservices, when launching my config service I'm getting an error that me or my friends do not get on different devices.
I've tried to clean and repackage the jar file, I'm using the same version of intelij as the other pc's, and I'm using the same jdk, language level. Also I have checked if the jar files are the same size the only difference i am seeing is that on the pc i am using i'm using a slightly newer version of apache maven 3.8.1 (not working pc) instead of 3.6.1(working pc), could this be the issue or would it be something else since these things tend to be backwards compatible.
how I launch my config service: java -jar config-service/target/config-service-0.0.1-SNAPSHOT.jar --spring.profiles.active=native
link to error: https://pastebin.com/UDRxZUkh
After reinstalling my windows it worked, but I did however install my windows on my HDD instead of my m.2 SSD and after then again reinstalling windows to my m.2 SSD have been getting the same error yet again.
So I'm not sure if this is actually an issue but it seems not to want to work on the my m.2 SSD (FYI this is the m.2: Kingston NV1 NVMe PCIe SSD 1000GBM.2 2280)

java on Linux arm64 downloads x86 libraries by mistake

I have a java 7 application that requires gtk libraries. I have installed those available on Ubuntu 18.04. However these don't seem to be compatible.
I have noticed that there is what appears to be a temp java library download folder:
~/.swt/lib/linux/aarch64
Where the application or java engine keeps downloading missing library dependencies for instance:
libswt-gtk-4234.so
But a file of these reveals it is a x86 library!!!
libswt-gtk-4234.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, stripped
I was wondering if this can be controlled and I can make java download the aarch64 libraries instead.
I have tried linking all the libraries in :
ln -s /usr/lib/jni/libswt-* ~/.swt/lib/linux/aarch64
But these libraries have different version numbers
libswt-gtk-4919.so instead of libswt-gtk-4234.so
and the x86 will end up downloading anyway.
I have tried manually linking libswt-gtk-4919.so to libswt-gtk-4234.so but the java application faults with UnsatisfiedLinkError: type errors.
I need to figure out how to make the java download the correct architecture libraries or find the correct libraries myself to place there.
I believe these libraries are part of eclipse (but not sure).

java 1.8 jar runs in Windows not Ubuntu, "could not find or load main class ..."

I have a jar file that runs fine from the Windows 10 command line, but is not working on my desktop Ubuntu 18.04 command line. The file was exported from Eclipse as a jar, and copied to Ubuntu. The project has about 30 classes over a half dozen packages. I've done some renaming in order to simplify things for this question.
This is the command used to run the jar:
java -jar myproject.jar
On Ubuntu, I get the error "Could not find or load main class com.a.b.LaunchThis"
Following are some of the things I've tried, based on suggestions from many similar posts. Sorry if this is a duplicate, but I couldn't find a workable answer.
I verified that the current Java is 1.8 using the commands:
$ update-alternatives --config java
and
$ java -version
openjdk version "1.8.0_252"
OpenJDK Runtime Environment (build 1.8.0_252-8u252-b09-1~18.04-b09)
OpenJDK 64-Bit Server VM (build 25.252-b09, mixed mode)
(I also verified that the program runs using java 8 on Windows.)
I ran the following check, which displays all the files, and confirmed that com/a/b/LaunchThis.class exists and is spelled exactly the same way.
java tj myproject.jar
The manifest seems correct.
Manifest-Version: 1.0
Main-Class: com.a.b.LaunchThis
I also tried unpacking the jar and running the program directly from the folder /myproject which contains both /com and META-INF. That should work, shouldn't it?
java com.a.b.LaunchThis
java com/a/b/LaunchThis
java com.a.b/LaunchThis
And from one level outside the myproject folder:
java myproject/com/a/b/LaunchThis
java myproject.com.a.b/LaunchThis
java myproject.com.a.b.LaunchThis
java myproject/com.a.b.LaunchThis
I am not totally clear on when the syntax requires . or /. The package command in "LaunchThis" which holds the main entry point is the following:
package com.a.b;
And in each of the above iterations I also tried including "-cp myproject.jar" as an option.
Always the exact same error message.
To be careful about not having a typo in the above, I first ran the ls command to make sure the file could be found (then edited this line to create the variants).
ls myproject.com.a.b.LaunchThis.class
Any suggestions as to what else to try?
For grins, running with Java11 gets this response:
Error: Could not find or load main class com.a.b.LaunchThis
Caused by: java.lang.NoClassDefFoundError: javafx/event/EventHandler
This makes some sense, because Java11 does not include JavaFX. But Java 8 does include EventHandler. It's been a part of Java since JavaFX 2.
UPDATE: I made two "Hello world" jars, one with and one without JavaFX on an Ubuntu installation of Eclipse running OpenJDK 8. To get the version with JavaFX to run, I downloaded the Oracle JDK 1.8, and linked to /lib/etc/jfxrt.jar as an "external jar" library. Both programs run in Eclipse but only the non-FX jar works. The FX jar gives exactly the same error message.
I think this pretty much establishes my problem to be one of not having JavaFX as part of OpenJDK 8.
My plan is to now try out two solutions: (1) copying the Oracle jfxrt.jar into the OpenJDK lib, (2) backloading OpenJFX 8.
Simply installing OpenJFX from repository loads OpenJFX 11, which is a little dated but will work with the OpenJDK 11.
The issue here is that the repository OpenJDK 8 on Ubuntu 18.04 does not include JavaFX. I'm guessing that the error message results from the fact that a JavaFX main() is located on a class that extends the JavaFX class Application.
How to fix this?
There are a number of things I tested.
First, one could use Oracle's JDK 1.8 instead. I ruled this out due to licensing issues.
Some sites suggested copying the jfxrt.jar from either the Oracle JDK 1.8 or from a working Java program that uses JavaFX and uses a self-contained JRE. I found that if I linked to the Oracle jfxrt.jar (located in the /jre/lib/ext folder) as an external jar, I could run a simple, "Hello world" javafx program in Eclipse. But I could not run the program after it was exported to a jar.
One answer on a related stackoverflow thread suggested copying over several additional files along with jfxrt.jar, which I tried. This did not work, so I'm not going to pass that on.
The solution that has been most successful came from a link provided in the comment by #dave_thomson_085, How do I get Java FX running with OpenJDK 8 on Ubuntu 18.04.2 LTS?.
This answer involves purging and reinstalling openjfx with an older version, and marking it to NOT be subjected to updates.
The code provided by Wolfgang Fahl (which he credits to Druidefix) follows:
apt purge openjfx
apt install openjfx=8u161-b12-1ubuntu2 libopenjfx-jni=8u161-b12-1ubuntu2 libopenjfx-java=8u161-b12-1ubuntu2
apt-mark hold openjfx libopenjfx-jni libopenjfx-java
Now, I can run and test jars containing Java8 using JavaFX that were built on my Windows Eclipse, on my Ubuntu desktop.
I do get a warning message which I haven't dealt with yet:
Gtk-Message: 13:25:40.829: Failed to load module "canberra-gtk-module"
But this is not preventing my programs from running.

Grails and javac opt

I'm using grails burning image plugin and it works fine on my Windows development environment and on the linux pre-production server.
Unfortunately, the production server is a FreeBsd server based on openJDK 1.7.0_65.
When I want to upload a picture which is a '.jpg' or '.jpeg' file, I get a com.sun.image.codec.jpeg.ImageFormatException.
After looking on stackoverflow, I found these two posts which seem to say that I have to add -XDignore.symbol.file option launching the java compiler.
Source:
import com.sun.image.codec.jpeg.*
Using internal sun classes with javac
How can I add this option to my grails application?
This sun package is not supported in OpenJDK. See these links for further reference:
Issue 28: fails under OpenJDK-7++
difference between Sun JDK and open JDK
Sadly, this renders the plugin virtually useless with OpenSDK's.
Edit: What I found as a solution is this plugin:
hd-image-utils
Fair warning thou, it has problems working with CMYK images.

Installing Java Runtime with Saltstack

I'm using Salt to configure a bunch of Centos machines (rpm-based) and I need to install the Java runtime. I've seen some discussion of doing this with Ubuntu-based machines but I wonder if anybody has done it on Redhat-based distros. The problem is getting past the "accept license" dialog without user intervention.
You can actually pre-seed the answers to those interactive questions. Here's a thread from the salt-users mailing list showing how: https://groups.google.com/d/msg/salt-users/95Q707FFWYo/CdcJN7FPpRAJ
There is this nice saltstack-formula/sun-java-formula
Formula to set up and configure Java JREs and JDKs from a tarball archive sourced via URL.
It can be installed using formulas documentation. I did not test this on a centos installation, but it uses tarball installation so maybe it would work. If someone tests it on a rvm based system, please comment here! ;)
I successfuly installed this on a debian machine with the following pillar:
java_home: /usr/lib/java
java:
source_url: http://download.oracle.com/otn-pub/java/jdk/7u79-b15/jre-7u79-linux-x64.tar.gz
jce_url: http://download.oracle.com/otn-pub/java/jce/7/UnlimitedJCEPolicyJDK7.zip
version_name: jdk1.7.0_79
prefix: /usr/share/java
dl_opts: -b oraclelicense=accept-securebackup-cookie -L
It installed java-jdk successfully, but it failed to install jce. I created issue #20 for this just in case, but I don't actually need jce (at least, not now).

Categories

Resources