Calling a C function from JAVA using the JNI
17 Message(s) by 5 Author(s) originally posted in java help
| From: csharpdotcom |
Date: Friday, October 19, 2007
|
Hi all, this is my first
posting here, and'd most appreciate some
kind help.
I need to pass a
string to the
Linux shell from JAVA, and because of
the restrictions of the "getRuntime()"
method , I'm trying to call up a
C
program from JAVA to which is I passed a string. The C program then
execute s the "system()"
function containing the string that I want to
pass to the Linux shell. The idea eventually is to execute the
code
as a
bean in Glassfish, but I'm having trouble
testing it out from the
CLI. There seems to be some problem with packages and
class path s.
The code is in a
file in a
folder tree , the last two levels being "/
com/corejsf", and in the programs I have the declaration
"package.com.corejsf;"
There are four
list ings as follows:
// Listing 1
package com.corejsf;
import JAVA.io.*;
import JAVA.util.*;
class CallUserBean {
public static void main(String[] args) {
UserBean bean = new UserBean();
String s = new String();
s = "Passing this string to UserBean";
System.out.println(s);
bean.setName(s);
}
}
Which calls the class in "UserBean.JAVA"
// Listing 2
package com.corejsf;
import JAVA.io.*;
import JAVA.util.*;
public class UserBean {
private String name;
private String
password ;
// PROPERTY: name
public String getName() { return name; }
public void setName(String newValue) {
System.out.println("Now in UserBean.setName()");
System.out.println(newValue);
String
command = new String();
command = "ls -lt > jnioutput.txt";
System.out.println("Sending the string - " + command + " - to
native code");
CallSystem.system(command);
name = newValue;
}
// PROPERTY: password
public String getPassword() { return password; }
public void setPassword(String newValue) { password = newValue; }
}
At the moment for the test only the methods "getName", "setPassword"
and "getPassword" aren't used. This in turns calls the class in
"CallSystem.JAVA":
Listing 3
package com.corejsf;
class CallSystem {
public static native void system(String s);
static {
System.loadLibrary("CallSystem");
}
}
Which calls the native C code in "CallSystem.c" ("UserBean.h" was
created with the "JAVAh" command as required):
Listing 4
#
include "UserBean.h"
#include
<stdio.h>
#include
<stdlib.h>
#include
<string.h>
JNI EXPORT void JNICALL JAVA_CallSystem_system(JNIEnv* env, jclass cl,
jstring jcommand) {
const
char *ccommand;
ccommand = (*env)->GetStringUTFChars(env, jcommand, NULL);
system(ccommand);
(*env)->ReleaseStringUTFChars(env, jcommand, ccommand);
}
which passes a string to the shell for execution. In this case a
simple test "ls" is executed, as you can see in listing 1.
After I compile the C code and
link in the shared
library ,
"libCallSystem.so", then compile the JAVA files with the "package
com.corejsf;" commented out in listings 1, 2 and 3, the code works
with the command:
"JAVA -DJAVA.library.path=. CallUserBean"
from inside /com/corejsf and
generate s the test file "jnioutput.txt"
and
write to it. This means that the native code is being called up
correctly. However, if I uncomment the package statements and
recompile the JAVA files using the statements "JAVAc -classpath
"../../" CallSystem.JAVA", and likewise for the other two files then
issue the command:
"JAVA -DJAVA.library.path=. -classpath ../../ CallUserBean"
the code in JAVA is executed correctly and generates the correct
output , but fails when it tries to call up the native code. I get the
message:
Exception in
thread "main" JAVA.lang.UnsatisfiedLinkError:
com.corejsf.CallSystem.system(LJAVA/lang/String;)V
at com.corejsf.CallSystem.system(Native Method)
at com.corejsf.UserBean.setName(UserBean.JAVA:19)
at CallUserBean.main(CallUserBean.JAVA:11)
At best similar messages are obtained when I try other combinations of
"-DJAVA.library.path" and "-classpath", or the program does not even
get that far. The fact that I can get it to work with the "package"
statements commented out means that it must be something to do with
the way the code is linked, and'd be most grateful to have some
advice on this.
If this can be sorted out, the idea is to remove the code from listing
one, recompile listings 2 and 3, put them in a ".war" file, and put
the shared library in the appropriate folder for Glasssfish. I have
in fact done this, and get the same error as above.
Christopher Sharp
| From: Gordon Beaton |
Date: Friday, October 19, 2007
|
wrote in message:
I need to pass a string to the Linux shell from JAVA, and because of
the restrictions of the "getRuntime()" method, I'm trying to call up
a C program from JAVA to which is I passed a string. The C program
then executes the "system()" function containing the string that I
want to pass to the Linux shell.
There is absolutely no need to use JNI for this. This should work:
String[] cmd = { "/bin/sh", "-c", "ls -lt > jnioutput.txt" };
Runtime.getRuntime().exec(cmd);
I'm guessing that you tried to use
redirection or quoting or other
shell features without specifying a shell explicitly. But since
Runtime.exec() does not
run your command in a shell unless you specify
one, those things are not available by default.
If that's "just an example" then post the
real original string you're
having trouble with, and the Runtime.exec() code you tried to use. I
can not really tell since your setName() method looks a little
nonsensical: I can not see how "ls -l" relates to the command in
question.
If all you need to do is get a file list into "jnioutput.txt" then you
really do not even need Runtime.exec, since you can get the same
information with File.listFiles() (etc) and then write to the file
with a PrintWriter.
Finally, your JNI
exception is due to the fact that you failed to
specify the fully qualified classname (i.e. *with* package name) when
you ran JAVAh to generate UserBean.h, and so your native code does not
match what's expected by the JVM.
/gordon--
| From: csharpdotcom |
Date: Friday, October 19, 2007
|
Hi Gordon,
Many thanks for your reply.
There is absolutely no need to use JNI for this. This should work:
String[] cmd = { "/bin/sh", "-c", "ls -lt > jnioutput.txt" };
Runtime.getRuntime().exec(cmd);
In fact that is exactly what I did in Glassfish, but it'll only work
with simple commands, and appears to fail when I include a "cd" in the
string. I'm just using this particular string to test things out.
I'm guessing that you tried to use redirection or quoting or other
shell features without specifying a shell explicitly. But since
Runtime.exec() does not run your command in a shell unless you specify
one, those things are not available by default.
If that's "just an example" then post the real original string you're
having trouble with, and the Runtime.exec() code you tried to use. I
can not really tell since your setName() method looks a little
nonsensical: I can not see how "ls -l" relates to the command in
question.
The original string gave the full path to a particular folder and
included the "cd" command, which caused the
server to fail.> If all you need to do is get a file list into "jnioutput.txt" then you
really do not even need Runtime.exec, since you can get the same
information with File.listFiles() (etc) and then write to the file
with a PrintWriter.
That isn't all I want to do, I'm just trying a simple "Hello World"
type example first to get the wringles out of the linking problems.
Finally, your JNI exception is due to the fact that you failed to
specify the fully qualified classname (i.e. *with* package name) when
you ran JAVAh to generate UserBean.h, and so your native code does not
match what's expected by the JVM.
/gordon
I've just tried "JAVAh -classpath ../../ UserBean" , but it does not
find the class. Obviously there is something simple that I'm missing.
Christopher Sharp
| From: csharpdotcom |
Date: Friday, October 19, 2007
|
OK, I have just got that part to work, many thanks, I typed:
JAVAh -classpath ../../ -o UserBean.h com.corejsf.UserBean
The "-classpath ../../" part because I have to go up in the tree as I
am in the folder with the code, and the output filename, otherwise I
get "com_corejsf_UserBean.h". I then compiled the C code with:
gcc -D_REENTRANT -fPIC -I/
<path-to-jdk>/include -I/
<path-to-jdk>/
include/linux -c CallSystem.c
which generated the
object file, then typed:
gcc -shared CallSystem.o -o libCallSystem.so
to create the shared library. However, on attempting to execute:
JAVA -DJAVA.library.path=. -classpath "../.." CallUserBean
I get the same link errors as before, so something is still wrong.
Christopher
| From: Gordon Beaton |
Date: Friday, October 19, 2007
|
wrote in message:
In fact that is exactly what I did in Glassfish, but it'll only
work with simple commands, and appears to fail when I include a "cd"
in the string. I'm just using this particular string to test things
out.
Again, please post a real example of a real command string, so I do not
need to make incorrect assumptions.
You can not use cd by itself (well you can, but its effect is lost when
the
process ends, and it certainly won't affect the JAVA application).
The same is true of system().
You can do this though:
String[] cmd = { "/bin/sh", "-c", "cd /tmp; ls -lt > jnioutput.txt" };
Runtime.getRuntime().exec(cmd);
In this example, "ls -lt > jnioutput" will run in the /tmp
directory .
Note that
semicolon is necessary to separate the different commands.
I've just tried "JAVAh -classpath ../../ UserBean" , but it does not
find the class. Obviously there is something simple that I'm
missing.
The name of the class is something like com.foo.UserBean, where the
first part comes from the package declaration in the JAVA
source , so
your command should be: "JAVAh com.foo.UserBean". If you need to
specify a classpath, you can do that too, but "UserBean" simply is not
the full name of the class.
I'm still convinced that Runtume.exec() is sufficient, but you need to
post a real example or I can not be more specific.
/gordon
--
| From: Gordon Beaton |
Date: Friday, October 19, 2007
|
wrote in message:
I get the same link errors as before, so something is still wrong.
After running JAVAh with the correct classname, did you modify the C
source so the native method matches the declaration in the new
header
file?
/gordon
--
| From: csharpdotcom |
Date: Friday, October 19, 2007
|
After running JAVAh with the correct classname, did you modify the C
source so the native method matches the declaration in the new header
file?
/gordon
--
In fact I sent a post where I had created the new header file with the
same name as before, and the C code compiled and linked OK, and I
created the shared library, but I'm still having trouble running the
JAVA. For some reason the post has not appeared yet.
If I do not get it solved in the next few minutes, it'll have to wait
until tomorrow morning, as I'm going out for the evening.
Christopher
| From: Gordon Beaton |
Date: Friday, October 19, 2007
|
wrote in message:
In fact I sent a post where I had created the new header file with
the same name as before, and the C code compiled and linked OK, and
I created the shared library, but I'm still having trouble running
the JAVA. For some reason the post has not appeared yet.
Whatever - I do not think you're reading what I have written.
It does not matter how many times you re-run JAVAh and recompile your
code. The important thing is that the name of the function itself in
the C source is correct with respect to the fully qualified name of
the class, i.e. with the package declaration.
The generated header file shows you what the correct name is, but only
if you run JAVAh correctly. Make sure you do not have old copies of the
header file or the shared library that might disturb your
observations.
For about the fifth time though, you can probably solve this with
Runtime.exec().
/gordon
--
| From: Lothar Kimmeringer |
Date: Friday, October 19, 2007
|
wrote in message:
String[] cmd = { "/bin/sh", "-c", "cd /tmp; ls -lt > jnioutput.txt" };
Better "ls /tmp && ls -lt > jnioutput.txt" to make sure that
the second part isn't executed if /tmp does not exist or is
restricted in access.
To make sure that the process does not
block STDERR must be
read or a 2>
/dev/null must be added to the ls.
If there are many commands to be executed I think a shell-script
should be considered, that is executed by JAVA the same way as
already shown. I really do not see the need for a JNI-Wrapper,
either.Regards, Lothar
--
Lothar Kimmeringer E-Mail: spamfang@xxxxxxxxxxx
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)
Always remember: The answer is forty-two, there can only be wrong
questions!
| From: Roedy Green |
Date: Saturday, October 20, 2007
|
On Fri, 19 Oct 2007 06:42:36 -0700, csharpdotcom
<cmsharp01@xxxxxxxxxxx>
wrote in message, quoted or indirectly quoted someone who said :
getRuntime()"
What you want to do sounds quite doable with an ordinary exec, which
is much simpler than using JNI. See
http://mindprod.com/jgloss/exec.html
It has many variations. Do not give up on it too quickly.
--
Roedy
Green Canadian Mind Products
The JAVA Glossary
http://mindprod.com
| From: csharpdotcom |
Date: Saturday, October 20, 2007
|
Hi Gordon and Roedy,
I have just logged on since yesterday and see some further very valuable
comments - many thanks.
Well I just got the JNI to work at last! On my way back from party
last night I had the idea of changing the name of the function in the
C program, and only just had the chance to try it out, and it worked!
What I did was first to create the ".h" file with the default name
generated by JAVAh, rather than giving it the name I want it to have
using the command:
JAVAh -classpath ../../ com.corejsf.UserBean
which creates the filename "com_corejsf_UserBean.h", then in
"CallSystem.c" I put in that filename in the "include" statement, but
then I changed the name of the function in the C program called from
JAVA from "JAVA_CallSystem_system" to
"JAVA_com_corejsf_CallSystem_system". That is the critical change
which caused the program to work.
Nowhere had I seen any
documentation on this detail, either in one of
my books or online. I'm now going to try this out with Glassfish, as
well as looking again at the "getRuntime()" method, links to which you
kindly sent me.
Christopher
| From: Gordon Beaton |
Date: Saturday, October 20, 2007
|
wrote in message:
Nowhere had I seen any documentation on this detail, either in one of
my books or online.
The JAVAh documentation is pretty clear on this point:
SYNOPSIS
JAVAh [options] fully-qualified-classname
DESCRIPTION
[...]
If the class passed to JAVAh is inside a package, the package name is
prepended to both the header file name and the structure name.
It's also in my *first* response to you in this thread.
/gordon
--
| From: Lew |
Date: Saturday, October 20, 2007
|
wrote in message:
wrote in message:
Nowhere had I seen any documentation on this detail, either in one of
my books or online.
The JAVAh documentation is pretty clear on this point:
SYNOPSIS
JAVAh [options] fully-qualified-classname
DESCRIPTION
[...]
If the class passed to JAVAh is inside a package, the package name is
prepended to both the header file name and the structure name.
It's also in my *first* response to you in this thread.
Yeah, when I read the OP's
On my way back from party
last night I had the idea of changing the name of the function in the
C program,
I thought to myself, "Self, surely we remember that advice from Gordon's
*first* response in this thread?"
to the OP: it seems a
bit strange to claim credit for having just, on 20
October, getting the idea that was given to you on 19 October.
--
Lew
| From: csharpdotcom |
Date: Saturday, October 20, 2007
|
Yes, OK, many thanks, somehow I missed the earlier comments - sorry
about that, but I was getting tired.
OK, I got the JNI to work with Glassfish, which is great. Given
http://mindprod.com/jgloss/exec.htm, in particular the "gotcha" that
commands like "dir" can not be executed directly I'm going through the
advice here on how to execute commands like "cd /home/csharp/tempdir;
ls -l > tempout.txt" etc. It appears to work from JAVA and C when run
in the CLI, which is reasonable, but not from Glassfish, so as advised
a shell should be invoked given the comments above.
And yes, it's probably better to use runtime().exec(), but the server
I'll eventually be working on has a lot of code in C and FORTRAN, so
I may still need to use JNI at some stage, and in any case it's useful
to know how to do this. For the time being I'm just testing out code
on my laptop.
Christopher
| From: Roedy Green |
Date: Monday, October 22, 2007
|
On Sat, 20 Oct 2007 08:56:45 -0700, csharpdotcom
<cmsharp01@xxxxxxxxxxx>
wrote in message, quoted or indirectly quoted someone who said :
Nowhere had I seen any documentation on this detail, either in one of
my books or online.
I had the same problem years ago -- detailed docs, but nothing on the
overall process and in general what was happening. It didn't make
sense until I got a textbook. See
http://mindprod.com/jgloss/jni.html
--
Roedy Green Canadian Mind Products
The JAVA Glossary
http://mindprod.com
| From: csharpdotcom |
Date: Monday, October 22, 2007
|
Hi Roedy,
OK, I also got the code to work using the ProcessBuilder class, which
is new in JAVA 5,
using your very helpful and extensive
website (plus lots of other
interesting things
outside of programming).
I modified my bean so that it is now:
package com.corejsf;
import JAVA.io.*;
import JAVA.util.*;
public class UserBean {
private String name;
private String password;
// PROPERTY: name
public String getName() { return name; }
public void setName(String newValue) {
System.out.println("Now in UserBean.setName()");
System.out.println(newValue);
try {
ProcessBuilder pb = new ProcessBuilder("/home/csharp/
shelltests/called-from-JAVA",
"first_argument", "second_argument",
"third_argument");
pb.directory(new File("/home/csharp"));
pb.start();
} catch (Exception e) {
System.out.println("Error executing script");
}
name = newValue;
}
// PROPERTY: password
public String getPassword() { return password; }
public void setPassword(String newValue) { password = newValue; }
}
with the JNI removed. It calls up the script in the path given above,
and the contents of
"called-from-JAVA" are:
#!/bin/sh
ls -lt > scriptout
echo $0 scriptout
echo $1 scriptout
echo $2 scriptout
echo $3 scriptout
which lists the files in the directory specified in the "directory()"
method. If I do not do
that, Glassfish writes to a file with
root privileges in its own
directory, which is highly
undesirable. One thing is that the statement
pb.directory("/home/csharp");
doesn't compile and generates a
syntax error, even though you've
something like that on
your website. The "new FILE(....)" has to be nested in the method
call for the code to
compile, at least on my computer, which has JAVA 6.
Anyway, although the JNI is not required here, as was mentioned
earlier, at least I now know
how to use it.
Christopher
| From: Roedy Green |
Date: Friday, October 26, 2007
|
On Fri, 19 Oct 2007 07:46:36 -0700, csharpdotcom
<cmsharp01@xxxxxxxxxxx>
wrote in message, quoted or indirectly quoted someone who said :
In fact that is exactly what I did in Glassfish, but it'll only work
with simple commands, and appears to fail when I include a "cd" in the
string. I'm just using this particular string to test things out.
that's because CD isn't an
executable but a command to the command
interpreter . See
http://mindprod.com/jgloss/exec.html for how to
spawn a command interpreter to
handle these.
--
Roedy Green Canadian Mind Products
The JAVA Glossary
http://mindprod.com
Next Message: 2d graphics and 2 other questions
Blogs related to Calling a C function from JAVA using the JNI
Java Database Connectivity Tutorial part 1
And the second option given was
using ODBC API which can solve the 1.a problem but again this ODBC API is also a Native API, so we have to use
JNI in our
Java applications which lead to the 1.b described problem.
...
JAVA JDBC QUESTIONS
The JDBC-ODBC bridge doesn't allow untrusted code to
call it for security reasons. This is good because it means that an untrusted applet that is downloaded by the browser can't circumvent
Java security by
calling ODBC.
...
Java Interview Questions, FAQs
JNI is an acronym of
Java Native Interface.
Using JNI we can
call functions which are written in other languages from
Java. Following are its advantages and disadvantages. Advantages: * You want to use your existing library which was
...
MSV8 JNI Functions?
I am trying to use
JNI to
call Java code from C++. I am getting kink errors because of missing library
functions, and I can't find the right libraries. Can anyone help? I am
using MS Visual Studio 8.
...
Re: Calling Java class files from PLSQL
As an update, Changing the Parameters of the
Java Function was not a good Idea. Back to
calling the Webservice from Oracle Forms, I managed to IMPORT the Element class from: org.w3c.dom.Element Now the issue is here, when I
call the
...
Excellent Senior JAVA Profile.... US Citizen
Web development
using Java, Autonomy, WebLogic, EJB,
JNI, RMI, XML, and IBM Websphere with servlets, JSP's, C and C++ for small E-Commerce Company. Also setup Microsoft IIS web server, SQL Server database tables, and performed various
...