I am trying to use O_direct in Java over JNI in Linux.
here is the java part:
public class jnitest {
static {
System.loadLibrary("jnitest"); // Load native library at runtime
// hello.dll (Windows) or libhello.so (Unixes)
}
private native void test();
private native void write(String path, byte[] chunkpayload);
public static void main(String[] args) {
try{
jnitest t=new jnitest();
byte[] chunk=new byte[5];
chunk[0]=1;
chunk[1]=2;
chunk[2]=3;
chunk[3]=4;
chunk[4]=5;
String s="outtest";
t.write(s,chunk);
}catch(Exception e){
System.out.println("fehler lol");
}
}
}
This is the C part:
#define _GNU_SOURCE
#include <jni.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include "jnitest.h"
#define BLOCKSIZE 512
JNIEXPORT void JNICALL Java_jnitest_write(JNIEnv *env, jobject obj, jstring jpath, jbyteArray jchunkpayload){
const char *path=(*env)->GetStringUTFChars(env, jpath, 0);
printf("path=%s \n",path);
(*env)->ReleaseStringUTFChars(env, jpath, path);
int len = (*env)->GetArrayLength (env,jchunkpayload);
printf("lenght= %d \n",len);
unsigned char* payload =(*env)->GetByteArrayElements(env,jchunkpayload,0);
int i=0;
for(i=0;i<len-1;i++){
printf("%02x \n",(int)payload[i]);
}
void *buffer;
int fd = open(path, O_SYNC|O_APPEND|O_CREAT|O_DIRECT);
posix_memalign(&buffer,BLOCKSIZE,BLOCKSIZE);
memcpy(buffer,payload,len);
write(fd,buffer,BLOCKSIZE);
printf("writen to file\n");
(*env)->ReleaseByteArrayElements(env,jchunkpayload,payload,JNI_ABORT);
free(buffer);
return;
}
JNIEXPORT void JNICALL Java_jnitest_test(JNIEnv *env, jobject obj){
printf("test\n");
return;
}
The program does not fail but the data is never written. I know I can use JNA, but i want to use JNI. Does somebody have a working example for writing a byte array in java over JNI using O_direct?
Related
Good Evening,
I have a c++ library file where I am trying to get a string value from but not by return.
This is for an Android Application build in Xamarin.
.cpp file
JNIEXPORT jint JNICALL Java_MyNamespace_ClassName_FunctionName
(JNIEnv *env, jclass obj, jstring returnString)
{
returnString = env->NewStringUTF("returnStringValue");
return 1;
}
In my Solution I have:
.cs File
namespace myNamespace
{
[DllImport("FolderName", EntryPoint = "Java_MyNamespace_ClassName_FunctionName")]
private static extern int FunctionName(IntPtr env, IntPtr thiz, IntPtr returnString);
void test_function()
{
IntPtr j = new IntPtr();
int status = FunctionName(JNIEnv.Handle, IntPtr.Zero, j);
string ss = j.ToString();
//Where I am expecting ss to be containing "returnStringValue".
}
}
Thanks #SushiHangover
We created a byte array
byte[] x = new byte[120];
Allocated that memory and copied it
System.Runtime.InteropServices.Marshal.AllocHGlobal()
System.Runtime.InteropServices.Marshal.Copy()
And then converted using:
System.Text.Encoding.UTF8.GetString()
Apparently this worked verry well.
Regards
After successfully fire command ndk-build I got .so file in my libs folder, then after some changes in gradle file I got native_libs.xml .idea/libraries folder.
Now I am accessing c function from my java code/ android activity. I am getting signal 11 error
My code is
C file
#include <jni.h>
#include <android/log.h>
#include <stdlib.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#define LOG_TAG "mylib"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
//JNIEXPORT jint JNICALL Java_com_xxx_xxx_activities_SplashActivity_logFileInfo(JNIEnv * env, jobject this, jstring filename);
jint Java_com_xxx_xxx_activities_TutorialsActivity_logFileInfo(JNIEnv * env, jobject this, jstring filename)
{
av_register_all();
AVFormatContext *pFormatCtx;
const jbyte *str;
str = (*env)->GetStringUTFChars(env, filename, NULL);
if(avformat_open_input(&pFormatCtx, str, NULL, NULL)!=0)
{
LOGE("Can't open file '%s'\n", str);
return 1;
}
else
{
LOGI("File was opened\n");
LOGI("File '%s', Codec %s",
pFormatCtx->filename,
pFormatCtx->iformat->name
);
}
return 0;
}
Loading and try to access method in java code is
private static native int logFileInfo(String filename);
static {
System.loadLibrary("framegrabber");
}
In OnCreate of activity
logFileInfo(file.getAbsolutePath());
Finally error at point logFileInfo(file.getAbsolutePath()); is
A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x106e in tid 8905 (Thread-20972)
Please replay if you have any solution, Thanking in advance.
I am working on android app in which I am using NDK for a small stuff. How to get sent string from java code to native C. I want to get a value in native C code which was sent from java code.
Here is my code which I wrote in activity
observer("test#gmail.com");
public native void observer(String email);
And Native code is this
void
Java_pl_pelotasplus_actionafteruninstall_MainActivity_observer(JNIEnv* env, jobject thiz) {
// I want to get email.. How to get
}
Thanks in advance.
Always use javah to generate the headers so you don't have any mistakes. Also recommend sticking it in a batch file so you can update with ease.
From source folder:
...\src> javah pl.pelotasplus.actionafteruninstall.MainActivity
Will generate the following .h file:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for pl_pelotasplus_actionafteruninstall_MainActivity */
#ifndef _Included_pl_pelotasplus_actionafteruninstall_MainActivity
#define _Included_pl_pelotasplus_actionafteruninstall_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: pl_pelotasplus_actionafteruninstall_MainActivity
* Method: observer
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_pl_pelotasplus_actionafteruninstall_MainActivity_observer
(JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif
Note the header does not include names for the parameters, so add them when implementing like this:
JNIEXPORT void JNICALL Java_pl_pelotasplus_actionafteruninstall_MainActivity_observer
(JNIEnv *env, jobject thiz, jstring email){
const char *nativeEmailString = (*env)->GetStringUTFChars(env, email, 0);
// use your string
(*env)->ReleaseStringUTFChars(env, email, nativeEmailString);
}
try this:
void Java_pl_pelotasplus_actionafteruninstall_MainActivity_observer(JNIEnv* env, jobject thiz, jstring jstr) {
char* email = jstringToCharArray(env, jstr);
}
jstringToCharArray:
char* jstringToCharArray(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("utf-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0)
{
rtn = (char*)malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
using JNI i'm trying to let a C program works under java. Here's the issue.
First i created the java class testcli.java:
public class testcli {
public native void stdErr();
public native int cliFromC();
static {
System.loadLibrary("ctest");
}
public static void main(String[] args) {
new testcli().stdErr();
new testcli().cliFromC();
}
Then compiled with
javac testcli.java
and created header with
javah testcli
after that created the librari ctest.c and compiled with
gcc -o libctest.so -shared -I/path/to/jni.h ctest.c -lc
and i got the libctest.so created, then added to bashrc the following code:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/Desktop/project2/thesys/src
and when i go to type
java testcli
it says me "No such file or directory".
I'm running java 8 oracle on a 32 bit machine with xubuntu 32 bit. Any ideas?
EDIT: ADDED right here the ctest.c code ( i think there might be some errors here)
#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <signal.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT 20000
#define LENGTH 512
JNIEXPORT void JNICALL Java_testcli_stdErr
(JNIEnv *env, jobject jobj)
{
const char *msg = NULL;
perror(msg);
exit(1);
}
JNIEXPORT jint JNICALL Java_testcli_cliFromC
(JNIEnv *env, jobject jobj)
{
/* Variable Definition */
int sockfd;
int nsockfd;
char revbuf[LENGTH];
struct sockaddr_in remote_addr;
//struct hostent *server; // per la parte scritta da me
/* Get the Socket file descriptor */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
fprintf(stderr, "ERROR: Failed to obtain Socket Descriptor! (errno = %d)\n",errno);
exit(1);
}
/* Fill the socket address struct */
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(PORT);
/*questo lo faccio così perchè localhost è 127.0.0.1, nel caso in cui debba recuperare l'ip dall'hostname uso la parte commentata sopra*/
inet_pton(AF_INET, "127.0.0.1", &remote_addr.sin_addr); //metto in remote_addr.sin_addr l'indirizzo ip 127.0.0.1 nel formato desiderato
bzero(&(remote_addr.sin_zero), 8);
/* Try to connect the remote */
if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
{
fprintf(stderr, "ERROR: Failed to connect to the host! (errno = %d)\n",errno);
exit(1);
}
else
printf("[Client] Connected to server at port %d...ok!\n", PORT);
/* Send File to Server */
//if(!fork())
//{
char* fs_name = "/home/elia/Desktop/project/tesi/PublicKey1";
char sdbuf[LENGTH];
printf("[Client] Sending %s to the Server... ", fs_name);
FILE *fs = fopen(fs_name, "r");
if(fs == NULL)
{
printf("ERROR: File %s not found.\n", fs_name);
exit(1);
}
bzero(sdbuf, LENGTH);
int fs_block_sz;
while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs)) > 0)
{
if(send(sockfd, sdbuf, fs_block_sz, 0) < 0)
{
fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno);
break;
}
bzero(sdbuf, LENGTH);
}
printf("Ok File %s from Client was Sent!\n", fs_name);
//}
close (sockfd);
printf("[Client] Connection lost.\n");
return (0);
}
Actually, everything is fine. The error comes from your stdErr() function, and it actually shows it is working. I get the same error. Why does this happen?
const char *msg = NULL;
perror(msg);
exit(1);
This prints the last error that was caused in the program. Now, you should only use perror() if you actually had an error in your program. Otherwise, it will print the message of whatever was in the errno variable previously.
errno may have been set by something inside Java itself. It may have been looking for some file, but its non-existence is not a problem for Java so it continued to work. But errno is set to that value and keeps that value, and that's what's printed in your program.
To prove this, I have added the following to your testcli.java:
public native void clearErr();
And my main is:
public static void main(String[] args) {
new testcli().clearErr();
new testcli().stdErr();
new testcli().cliFromC();
}
And to ctest.c I added:
JNIEXPORT void JNICALL Java_testcli_clearErr
(JNIEnv *env, jobject jobj)
{
errno = 0;
}
Did the javac, javah and gcc again, ran the program, and now I get:
Success
Note that your third method is not called because of the exit() in stdErr().
first of all i'd like to point the fact i'm not too expert with Java and less with C, after that, i'm using JNI to use a C program which is a client application for sockets.
First i've created the main class testcli:
public class testcli {
public native void stdErr();
public native int cliFromC();
static {
System.loadLibrary("ctest");
}
public static void main(String[] args) {
new testcli().stdErr();
new testcli().cliFromC();
}
}
After that i compiled with
javac testcli.java
then created the header file
javah testcli
At this point i've copied the strings i needed and pasted in the new file ctest:
#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <signal.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT 20000
#define LENGTH 512
JNIEXPORT void JNICALL Java_testcli_stdErr
(JNIEnv *, jobject)
{
perror(msg);
exit(1);
}
JNIEXPORT jint JNICALL Java_testcli_cliFromC
(JNIEnv *, jobject)
{
/* Variable Definition */
int sockfd;
int nsockfd;
char revbuf[LENGTH];
struct sockaddr_in remote_addr;
//struct hostent *server; // per la parte scritta da me
/* Get the Socket file descriptor */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
fprintf(stderr, "ERROR: Failed to obtain Socket Descriptor! (errno =%d)\n",errno);
exit(1);
}
/* Fill the socket address struct */
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(PORT);
/*questo lo faccio così perchè localhost è 127.0.0.1, nel caso in cui debba recuperare l'ip dall'hostname uso la parte commentata sopra*/
inet_pton(AF_INET, "127.0.0.1", &remote_addr.sin_addr); //metto in remote_addr.sin_addr l'indirizzo ip 127.0.0.1 nel formato desiderato
bzero(&(remote_addr.sin_zero), 8);
/* Try to connect the remote */
if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
{
fprintf(stderr, "ERROR: Failed to connect to the host! (errno = %d)\n",errno);
exit(1);
}
else
printf("[Client] Connected to server at port %d...ok!\n", PORT);
/* Send File to Server */
//if(!fork())
//{
char* fs_name = "/home/elia/Desktop/project/tesi/PublicKey1";
char sdbuf[LENGTH];
printf("[Client] Sending %s to the Server... ", fs_name);
FILE *fs = fopen(fs_name, "r");
if(fs == NULL)
{
printf("ERROR: File %s not found.\n", fs_name);
exit(1);
}
bzero(sdbuf, LENGTH);
int fs_block_sz;
while((fs_block_sz = fread(sdbuf, sizeof(char), LENGTH, fs)) > 0)
{
if(send(sockfd, sdbuf, fs_block_sz, 0) < 0)
{
fprintf(stderr, "ERROR: Failed to send file %s. (errno = %d)\n", fs_name, errno);
break;
}
bzero(sdbuf, LENGTH);
}
printf("Ok File %s from Client was Sent!\n", fs_name);
//}
close (sockfd);
printf("[Client] Connection lost.\n");
return (0);
}
At this point i added to my bashrc those lines:
C_INCLUDE_PATH=/usr/lib/jvm/java-8-oracle/include/
C_INCLUDE_PATH=/usr/lib/jvm/java-8-oracle/include/linux/
export C_INCLUDE_PATH
Saved and wrote in terminal
ECHO $C_INCLUDE_PATH
and it gave me only
/usr/lib/jvm/java-8-oracle/include/linux/
which i don't know if it's good or not cause first string is missing. After it i try to compile my ctest.c with this command line:
gcc -o libctest.so -shared -I/usr/lib/jvm/java-8-oracle/include ctest.c -lc
and have this errors, which i completely don't understand.
ctest.c: In function ‘Java_testcli_stdErr’:
ctest.c:19: error: parameter name omitted
ctest.c:19: error: parameter name omitted
ctest.c:22: error: ‘msg’ undeclared (first use in this function)
ctest.c:22: error: (Each undeclared identifier is reported only once
ctest.c:22: error: for each function it appears in.)
ctest.c: In function ‘Java_testcli_cliFromC’:
ctest.c:27: error: parameter name omitted
ctest.c:27: error: parameter name omitted
I'm figurin out that completely copy-pasting my working client.c into the ctest.c not work
well but i sincerely don't know how to fix it. One thing to know is that C code works alone, with obvious
void error(const char *msg)
before the first brace bracket and
int main(int argc, char *argv[])
before second one. If some1 can help it's really really appreciated. Thx very much for the effort!!
Cheers
JNIEXPORT void JNICALL Java_testcli_stdErr
(JNIEnv *, jobject)
{
perror(msg);
exit(1);
}
The first thing wrong here is that your parameters don't have names. While this can be effectively ignored with the proper compiler flags, I wouldn't recommend it. You should give your parameters names, e.g.:
JNIEXPORT void JNICALL Java_testcli_stdErr
(JNIEnv* env, jobject obj)
...
The next issue is perror(msg);, as msg isn't declared nor defined. Did you forget to pass it into your function? Is it supposed to be a global variable? Either way, it doesn't exist, and you can't print something that doesn't exist.