Here is the c header file:
idevice_error_t idevice_get_device_list(char ***devices, int *count);
idevice_error_t idevice_device_list_free(char **devices);
Here is the JNA that JNAerator generated for me:
int idevice_get_device_list(PointerByReference devices, IntBuffer count);
int idevice_device_list_free(PointerByReference devices);
Here is how I'm using it. It's in Kotlin, but it's equivalent to what you'd do in Java too:
fun getDeviceList(): List<String> {
val deviceUuidsPbr = PointerByReference()
val deviceSizeBuffer = IntBuffer.wrap(IntArray(1))
val resultInt = LibIMobileDeviceLibrary.INSTANCE.idevice_get_device_list(deviceUuidsPbr, deviceSizeBuffer)
val size = deviceSizeBuffer.get()
logger.v {"getDeviceList $resultInt, Size: $size" }
val stringArrayP = deviceUuidsPbr.value
val devices = stringArrayP
.getStringArray(0, size)
.toList()
logger.v { "Devices: $devices" }
LibIMobileDeviceLibrary.INSTANCE.idevice_device_list_free(deviceUuidsPbr)
return devices
}
Everything blows up when I free memory:
LibIMobileDeviceLibrary.INSTANCE.idevice_device_list_free(deviceUuidsPbr)
The free wants a char **devices, but I'm submitting a char ***devices. How should I get the PointerByReference into the correct format?
A PointerByReference is just a pointer (to a pointer). And the two C functions being mapped simply have an extra level of indirection.
Consider this function definition:
device_get_device_list(char ***devices, int *count);
idevice_device_list_free(char **devices);
now consider foo defined to be *devices and the original functions simplify to:
device_get_device_list(char **foo, int *count);
idevice_device_list_free(char *foo);
And if you define bar as *foo you get:
device_get_device_list(char *bar, int *count);
idevice_device_list_free(char bar);
So you cannot pass the PointerByReference you are receiving from device_get_device_list (*bar) directly to idevice_device_list_free (which needs bar); you pass its pointed-to value (which happens to be a pointer to another pointer, but that doesn't matter.)
In Java you would simply change the argument in your idevice_device_list_free() call from deviceUuidsPbr to deviceUuidsPbr.getValue().
I am not a Kotlin user but it would seem based on your other code that you need deviceUuidsPbr.value there.
i have some .dll files which has been used by some VB program now i have to port the application in java but the .dll file provider is not providing any jars or API for java platform.
since my requirement is to access the code written in those .dll files in java class , is there any solution to aceesss the .dll ??
thasks in advance...:)
i also got one solution
package jnahelloworldtest;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Platform;
import com.sun.jna.*;
/** Simple example of native library declaration and usage. */
public class Main {
public interface simpleDLL extends Library {
simpleDLL INSTANCE = (simpleDLL) Native.loadLibrary(
(Platform.isWindows() ? "simpleDLL" : "simpleDLLLinuxPort"), simpleDLL.class);
// it's possible to check the platform on which program runs, for example purposes we assume that there's a linux port of the library (it's not attached to the downloadable project)
byte giveVoidPtrGetChar(Pointer param); // char giveVoidPtrGetChar(void* param);
int giveVoidPtrGetInt(Pointer param); //int giveVoidPtrGetInt(void* param);
int giveIntGetInt(int a); // int giveIntGetInt(int a);
void simpleCall(); // void simpleCall();
}
public static void main(String[] args) {
simpleDLL sdll = simpleDLL.INSTANCE;
sdll.simpleCall(); // call of void function
int a = 3;
int result1 = sdll.giveIntGetInt(a); // calling function with int parameter&result
System.out.println("giveIntGetInt("+a+"): " + result1);
String testStr = "ToBeOrNotToBe";
Memory mTest = new Memory(testStr.length()+1); // '+1' remember about extra byte for \0 character!
mTest.setString(0, testStr);
String testReturn = mTest.getString(0); // you can see that String got properly stored in Memory object
System.out.println("String in Memory:"+testReturn);
Memory intMem = new Memory(4); // allocating space
intMem.setInt(0, 666); // setting allocated memory to an integer
Pointer intPointer = intMem.getPointer(0);
int int1 = sdll.giveVoidPtrGetInt(Pointer.NULL); // passing null, getting default result
System.out.println("giveVoidPtrGetInt(null):" + int1); // passing int stored in Memory object, getting it back
int int2 = sdll.giveVoidPtrGetInt(intMem);
//int int2 = sdll.giveVoidPtrGetInt(intPointer); causes JVM crash, use memory object directly!
System.out.println("giveVoidPtrGetInt(666):" + int2);
byte char1 = sdll.giveVoidPtrGetChar(Pointer.NULL); // passing null, getting default result
byte char2 = sdll.giveVoidPtrGetChar(mTest); // passing string stored in Memory object, getting first letter
System.out.println("giveVoidPtrGetChar(null):" + (char)char1);
System.out.println("giveVoidPtrGetChar('ToBeOrNotToBe'):" + (char)char2);
}
}
but this could not helped me out.
You can use DependencyWalker to figure out what functions are being exported from the DLL.
VB uses the stdcall calling convention, so you'll need to extend from StdCallLibrary rather than Library, and you'll probably need to initialize your library with a StdCallFunctionMapper, since your DLL's exported methods likely decorate the method names with a #NN suffix.
Map options = new Map();
options.put(Library.OPTION_FUNCTION_MAPPER, new StdCallFunctionMapper());
SimpleDLL dll = (SimpleDLL)Native.loadLibrary("simpleDLL", SimpleDLL.class, options);
Given this C++ Code:
void LoadData(char** myVar)
{
std:: string str("[Really Long String Here]");
unsigned int size = str.length() + 1;
*myVar = new char[size];
strncpy(*myVar, str.c_str(), size);
}
And this JNA Java:
Pointer myVar = new Memory(Pointer.SIZE);
this.Lib.LoadData(myVar);
this.someVar = myVar.getPointer(0).getString(0);
I'm having memory leaks, as I understand it, getPointer(0) should create a pointer object that should be released on finalize(), but it seems to not be.
Am I missing something? This seems up to spec... and I can run the function above with no leaks in C++ fine.
I call the Java code in a loop to test the leak, I've tried putting in pauses, and manually calling the GC, also it'll bloat to gigabytes rather quickly this way.
I've been banging my head against this for a few days now and it sucks to get hung up on something so trivial as attempting to free memory.As far as I can tell I can only manually free memory in Java if I have the address, but I can't see how I'd get that.
Edit:
Nevermind, I don't even think there is a way to do manually free through JNA without extending it...
Add this function to the C++ library...
void FreeData(char** myVar)
{
delete [] *myVar;
}
And then make this the JNA code
Pointer myVar = new Memory(Pointer.SIZE);
this.Lib.LoadData(myVar);
this.someVar = myVar.getPointer(0).getString(0);
this.Lib.FreeData(myVar);
This way you allocate and delete the memory in C++.
Allocate in the caller, not the callee.
For example:
int LoadData(char* buf, int maxlen) {
std:: string str("[Really Long String Here]");
strncpy(buf, str.c_str(), maxlen);
if (str.length() < maxlen)
return str.length();
return maxlen;
}
Then when you call from Java, pass in a byte[] of the appropriate size. Note that this implementation is potentially very inefficient, but the idea is that you don't generally want to be allocating memory in one context and deallocating it in another.
Instead of
myVar = new char[size]
use
*myVar = malloc(size);
strncpy(*myVar, str.c_str(), size);
Arrays need to be deleted like:
delete [] *myVar;
JNA prolly doesn't know to do that.
I am trying to create bindings to the FUSE library using JNA, but I have hit a snag along the road. I have minimized the code as much as possible to make it digestible here.
The FUSE library comes with a few example filesystems written in C. The simplest of them is hello.c. The following is a minimized version of its code to simply a few prints in the filesystem functions:
hello.c:
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2007 Miklos Szeredi <miklos#szeredi.hu>
This program can be distributed under the terms of the GNU GPL.
See the file COPYING.
gcc -Wall hello.c -o hello `pkg-config fuse --cflags --libs`
*/
#define FUSE_USE_VERSION 26
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
static int hello_getattr(const char *path, struct stat *stbuf)
{
printf("getattr was called\n");
return 0;
}
static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi)
{
printf("readdir was called\n");
return 0;
}
static int hello_open(const char *path, struct fuse_file_info *fi)
{
printf("open was called\n");
return 0;
}
static int hello_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
{
printf("read was called\n");
return 0;
}
static struct fuse_operations hello_oper = {
.getattr = hello_getattr,
.readdir = hello_readdir,
.open = hello_open,
.read = hello_read,
};
int main(int argc, char *argv[])
{
return fuse_main_real(argc, argv, &hello_oper, sizeof(hello_oper), NULL);
}
This can be compiled using gcc -Wall hello.c -o hello -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse -pthread -lfuse -lrt -ldl
And invoked with ./hello.c -f /some/mount/point
The -f flag is to make it stay in the foreground so that you can see the printf()'s working.
All of this works well, you can see the printf()'s executing properly. I am trying to replicate the same thing in Java using JNA. Here is what I came up with:
FuseTemp.java:
import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
public class FuseTemp
{
public static interface Fuse extends Library
{
int fuse_main_real(int argc, String[] argv, StructFuseOperations op, long size, Pointer user_data);
}
#SuppressWarnings("unused")
public static class StructFuseOperations extends Structure
{
public static class ByReference extends StructFuseOperations implements Structure.ByReference
{
}
public Callback getattr = new Callback()
{
public int callback(final String path, final Pointer stat)
{
System.out.println("getattr was called");
return 0;
}
};
public Callback readlink = null;
public Callback mknod = null;
public Callback mkdir = null;
public Callback unlink = null;
public Callback rmdir = null;
public Callback symlink = null;
public Callback rename = null;
public Callback link = null;
public Callback chmod = null;
public Callback chown = null;
public Callback truncate = null;
public Callback utime = null;
public Callback open = new Callback()
{
public int callback(final String path, final Pointer info)
{
System.out.println("open was called");
return 0;
}
};
public Callback read = new Callback()
{
public int callback(final String path, final Pointer buffer, final long size, final long offset, final Pointer fi)
{
System.out.println("read was called");
return 0;
}
};
public Callback write = null;
public Callback statfs = null;
public Callback flush = null;
public Callback release = null;
public Callback fsync = null;
public Callback setxattr = null;
public Callback getxattr = null;
public Callback listxattr = null;
public Callback removexattr = null;
public Callback opendir = null;
public Callback readdir = new Callback()
{
public int callback(final String path, final Pointer buffer, final Pointer filler, final long offset,
final Pointer fi)
{
System.out.println("readdir was called");
return 0;
}
};
public Callback releasedir = null;
public Callback fsyncdir = null;
public Callback init = null;
public Callback destroy = null;
public Callback access = null;
public Callback create = null;
public Callback ftruncate = null;
public Callback fgetattr = null;
public Callback lock = null;
public Callback utimens = null;
public Callback bmap = null;
public int flag_nullpath_ok;
public int flag_reserved;
public Callback ioctl = null;
public Callback poll = null;
}
public static void main(final String[] args)
{
final String[] actualArgs = { "-f", "/some/mount/point" };
final Fuse fuse = (Fuse) Native.loadLibrary("fuse", Fuse.class);
final StructFuseOperations.ByReference operations = new StructFuseOperations.ByReference();
System.out.println("Mounting");
final int result = fuse.fuse_main_real(actualArgs.length, actualArgs, operations, operations.size(), null);
System.out.println("Result: " + result);
System.out.println("Mounted");
}
}
The definition of the the fuse_operations struct can be found here.
This can be compiled using: javac -cp path/to/jna.jar FuseTemp.java
And invoked using java -cp path/to/jna.jar:. FuseTemp
jna.jar is available here.
The error that comes up is: fusermount: failed to access mountpoint /some/mount/point: Permission denied.
I am executing both programs as the same user with the same permissions on the same mountpoint folder, and I am in the fuse group. I am using:
Linux kernel 3.0.0
FUSE 2.8.4
OpenJDK 1.6.0_23
JNA 3.4.0
So my question is: What exactly is different between these two programs (hello.c and FuseTemp.java), and how to make them do the same thing?
Thanks in advance.
Edit: Here is some additional info.
Initial stat of the mountpoint:
File: `/some/mount/point'
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: 803h/2051d Inode: 540652 Links: 2
Access: (0777/drwxrwxrwx) Uid: ( 1000/ myusername) Gid: ( 1000/ myusername)
Output I get from running the Java program as regular user:
Mounting
fusermount: failed to access mountpoint /some/mount/point: Permission denied
Result: 1
Mounted
(program exits with return code 0)
After this, trying to execute stat gives the following error message:
stat: cannot stat/some/mount/point': Transport endpoint is not connected`
That is because the Java program isn't running anymore, so fuse cannot call its callbacks. To unmount, if I try fusermount -u /some/mount/point, I get:
fusermount: entry for /some/mountpoint not found in /etc/mtab
And if I try sudo fusermount -u /some/mount/point, the mountpoint is successfully unmounted and there is no output from fusermount. /etc/mtab is chmod'd 644 (-rw-r--r--) so my user can read it, but it doesn't contain /some/mount/point. After a successful unmount, the mountpoint is back to its old permissions (777 directory).
Now, running the java program as root:
Mounting
Result: 1
Mounted
(program exits with return code 0)
After that, stating /some/mount/point shows that is has not been modified, i.e. it is still a 777 directory.
I have also rewritten FuseTemp.java to include all Callbacks as Callbacks instead of Pointers. The behavior is the same, however.
I looked at fuse's source code and the error code 1 can be returned at multiple points throughout the execution. I will pinpoint where exactly is it failing on the fuse side and report back here.
Now for hello.c: running it as regular user, starting with the same permissions on /some/mount/point and passing it the arguments -f and /some/mount/point, the program doesn't print any output at first but keeps running. When running stat on the mountpoint, the program prints
getattr was called
like it should. stat returns an error, but that's simply because hello.c's getattr function doesn't give it any information, so no problems there. After executing fusermount -u /some/mount/point as regular user, the program exits with return code 0 and the unmount is successful.
Running it as root, starting with the same permissions on /some/mount/point and passing it the arguments -f and /some/mount/point, the program doesn't print any output at first but keeps running. When running stat on the mountpoint, I get a permission error because I am not root. When running stat on it as root, the program prints
getattr was called
like it should. Executing fusermount -u /some/mount/point as regular user yields
fusermount: entry for /some/mount/point not found in /etc/mtab
Executing fusermount as root, the program exits with return code 0 and the unmount is successful.
Found it. While the error was really silly in retrospect, it wasn't easy to spot.
The solution: Fuse's fuse_main_real method's first argument is an argument list. In this list, it expects argument 0 to be the filesystem name, or some meaningful program name. Thus, instead of
final String[] actualArgs = { "-f", "/some/mount/point" };
It should have been
final String[] actualArgs = { "programName", "-f", "/some/mount/point" };
This also means that you can't use the argument list that Java gives you in your main method, since that doesn't include the program name either.
Why it matters: fuse actually does its own argument parsing and calls /bin/mount passing it the following arguments:
--no-canonicalize -i -f -t fuse.(arg 0) -o (options) (mountpoints) ...
As such, if you give if -f /some/mount/point as argument list, fuse will try to run:
/bin/mount --no-canonicalize -i -f -t fuse.-f -o rw,nosuid,nodev /some/mount/point
And mount doesn't like "fuse.-f" and will complain.
How it was found: Adding a bunch of printf() inside fuse's source code to figure out where exactly things were failing: in /lib/mount_util.c at line 82:
execl("/bin/mount", "/bin/mount", "--no-canonicalize", "-i",
"-f", "-t", type, "-o", opts, fsname, mnt, NULL);
I apologise for assuming the error was due to it being Java-related or JNA-related or permissions-related. I will edit the question title and tags to reflect this. (In my defense, the error fuse was returning ("Permission denied") certainly wasn't helpful!)
Thank you for your assistance ee. and technomage, and again I apologise for taking away a chunk of your time because of what turned out to be a silly mistake.
Regarding the permission denied issue when running the jar...I am sure it is Java security permission thing is going on here to explain why no exception is caught when running in superuser mode but permission denied exception is caught when running in non-superuser mode.
From what I can understand, Java has a layer of security unlike the standard C program (except for some C libraries that may include security checks, just like .NET managed C++ libraries). Even though the file manipulation functions are coming from libfuse.so, it may also call Linux system kernel calls that may be executed within system kernel memory space. Since it is now running via Java where Java has to load/map all library functions including system calls into memory. If Java finds out the memory map occurs in system kernel memory space rather than user memory space during execution, it will refer its security manager to check against the current user state of the Java program.
Otherwise, the permission denied error may actually come from fuse trying to access a mount point that is restricted from the normal user which is an expected behavior. Then, this has nothing to do with Java. But, this error shall also occur in C program as well. But, from your post and comments, it doesn't tell that much.
However, running the program as root didn't cause the error to appear.
Alas, it didn't seem to do anything: It just said "Mounting" and
"Mounted" instantly. So it does go up to completion, but the
fuse_main_real call returns instantly. The number it returns is 1.
That is some progress, but the program needs to be runnable as a
regular user like hello.c can.
On the other hand, based on your recent comment above, it seems that your function pointer (callback) fields in StructFuseOperations structure are not working to "fire up" any fuse event that fuse may invoke.
Note: I assume that the "erroneous" main Java program displays "Mounting" and "Mounted" and nothing else in between them which actually involves a call to fuse_main_real method that doesn't fire up any fuse event but a return code of 1 when running the program in the superuser mode. I haven't tried the code in the post since I don't have access to Linux OS right now.
Update: from this point onwards, the discussion about callback padding in a JNA structure is no longer valid after the recent post update made by OP: https://stackoverflow.com/revisions/e28dc30b-9b71-4d65-8f8a-cfc7a3d5231e/view-source
Based on the given link, fuse_operations Struct Reference, you only focus on a few fields of the C structure as follows:
static struct fuse_operations hello_oper = {
int (getattr*)(const char *path, struct stat *stbuf);
/** some 12 skipped callbacks in between **/
int (open*)(const char *path, struct fuse_file_info *fi);
int (read*)(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi)
/** some 10 skipped callbacks in between **/
int (readdir*)(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi);
/** some 11 skipped callbacks in between **/
unsigned int flag_nullpath_ok;
unsigned int flag_reserved;
/** some 2 skipped callbacks in between **/
};
However, it seems that you are trying to skip a few callback fields with padding. Therefore, to maintain the order of how the callback fields are laid out in fuse_operations structure, you apply the Pointer type to each callback field that you have skipped. However, by assuming a simple Pointer field for these skipped structure fields, you have removed the vital information about the callback for each field: its callback signature.
From JNA API Overview:
Callbacks (Function Pointers)
JNA supports supplying Java callbacks to native code. You must define
an interface that extends the Callback interface, and define a single
callback method with a signature that matches the function pointer
required by the native code. The name of the method may be something
other than "callback" only if there is only a single method in the
interface which extends Callback or the class which implements
Callback. The arguments and return value follow the same rules as for
a direct function invocation.
If the callback returns a String or String[], the returned memory will
be valid until the returned object is GC'd.
Following is what is suggested in the overview:
// Original C code
struct _functions {
int (*open)(const char*,int);
int (*close)(int);
};
// Equivalent JNA mapping
public class Functions extends Structure {
public static interface OpenFunc extends Callback {
int invoke(String name, int options);
}
public static interface CloseFunc extends Callback {
int invoke(int fd);
}
public OpenFunc open;
public CloseFunc close;
}
...
Functions funcs = new Functions();
lib.init(funcs);
int fd = funcs.open.invoke("myfile", 0);
funcs.close.invoke(fd);
However, it doesn't suggest a way to properly skip the callbacks with padding technique in a structure especially when it is too large and you don't want to define every callback that you are not interested with. Maybe, it is not warranted and may cause undefined behavior like what you are facing...
Probably, instead of Pointer for each callback field that you want to pad, you can use Callback field, maintain its field name as in the specification. You may or may not initialize it with the null value (I haven't tried this; possibly it may not work).
Update:
It seems that my suggestion above can work based on the unrelated JNA solution by tgdavies in C callback with JNA makes JRE crash where he padded those callback fields he wasn't interested with simple Callback type but the matching callback field names remained intact in the sp_session_callbacks Structure.
I guess, because of the improper fuse_operations structure, fuse_main_real is unable to fire up the expected fuse event that you are interested with.
I have a class in C++ which takes an std::ostream as an argument in order to continuously output text (trace information). I need to get this text over to the Java side as efficiently as possible. What's the best way to do this? I was thinking of using a direct buffer, but another method would be to take all the function calls across to Java and do all the processing there, but it seems that I'd need a lot of JNI calls.
If an example could be shown of the exact implementation method, it would be very helpful, or if some code exists already to do this (perhaps part of another project). Another help would be to connect it up directly to a standard Java streaming construct, such that the entire implementation was completely transparent to the developer.
(Edit: I found Sharing output streams through a JNI interface which seems to be a duplicate, but not really of much help -- he didn't seem to find the answer he was looking for)
The std::ostream class requires a std::streambuf object for its output. This is used by the fstream and stringstream classes, which use the features of ostream by providing a custom implementation of the streambuf class.
So you can write your own std::streambuf implementation with an overwritten overflow method, buffer the incomming chars in an internal stringbuffer. Every x calls or on eof/newline generate an java-string and call the print method of your java PrintStream.
An incomplete example class:
class JavaStreamBuff : std::streambuf
{
std::stringstream buff;
int size;
jobject handle;
JNIEnv* env
//Ctor takes env pointer for the working thread and java.io.PrintStream
JavaStreamBuff(JNIEnv* env, jobject jobject printStream, int buffsize = 50)
{
handle = env->NewGlobalRef(printStream);
this->env = env;
this->size = size;
}
//This method is the central output of the streambuf class, every charakter goes here
int overflow(int in)
{
if(in == eof || buff.size() == size)
{
std::string blub = buff.str();
jstring do = //magic here, convert form current locale unicode then to java string
jMethodId id = env->(env->GetObjectClass(handle),"print","(java.lang.String)V");
env->callVoidMethod(id,handle,do);
buff.str("");
}
else
{buff<<in;}
}
virtual ~JavaStreamBuff()
{
env->DeleteGlobalRef(handle);
}
}
Missing:
Multithread support (the env pointer is only valid for the jvm thread)
Error handling (checking for java exceptions thrown)
Testing(written within the last 70 min)
Native java method to set the printstream.
On the java side you need a class to convert the PrintStream to a BufferedReader.
There have to be some bugs there, haven't spend enough time to work on them.
The class requires all access to be from the thread it was created in.
Hope this helps
Note
I got it to work with visual studio but I can't get it to work with g++, will try to debug that later.
Edit
Seems that I should have looked for a more official tutorial on this bevore posting my answer, the MSDN page on this topic derives the stringbuffer in a different way.
Sorry for posting this without testing it better :-(.
A small correction to the code above in a more or less unrelated point: Just implement InputStream with a custom class and push byte[] arrays instead of Strings from c++.
The InputStream has a small interface and a BufferedReader should do most of the work.
Last update on this one, since im unable to get it to work on linux, even with the comments on the std::streambuf class stating that only overflow has to be overwritten.
This implementation pushes the raw strings into an inputstream, which can be read from by an other thread. Since I am too stupid to get the debugger working its untested, again.
//The c++ class
class JavaStreamBuf :public std::streambuf
{
std::vector<char> buff;
unsigned int size;
jobject handle;
JNIEnv* env;
public:
//Ctor takes env pointer for the working thread and java.io.PrintStream
JavaStreamBuf(JNIEnv* env, jobject cppstream, unsigned int buffsize = 50)
{
handle = env->NewGlobalRef(cppstream);
this->env = env;
this->size = size;
this->setbuf(0,0);
}
//This method is the central output of the streambuf class, every charakter goes here
virtual int_type overflow(int_type in = traits_type::eof()){
if(in == std::ios::traits_type::eof() || buff.size() == size)
{
this->std::streambuf::overflow(in);
if(in != EOF)
buff.push_back(in);
jbyteArray o = env->NewByteArray(buff.size());
env->SetByteArrayRegion(o,0,buff.size(),(jbyte*)&buff[0]);
jmethodID id = env->GetMethodID(env->GetObjectClass(handle),"push","([B)V");
env->CallVoidMethod(handle,id,o);
if(in == EOF)
env->CallVoidMethod(handle,id,NULL);
buff.clear();
}
else
{
buff.push_back(in);
}
return in;
}
virtual ~JavaStreamBuf()
{
overflow();
env->DeleteGlobalRef(handle);
}
//The java class
/**
*
*/
package jx;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* #author josefx
*
*/
public class CPPStream extends InputStream {
List<Byte> data = new ArrayList<Byte>();
int off = 0;
private boolean endflag = false;
public void push(byte[] d)
{
synchronized(data)
{
if(d == null)
{
this.endflag = true;
}
else
{
for(int i = 0; i < d.length;++i)
{
data.add(d[i]);
}
}
}
}
#Override
public int read() throws IOException
{
synchronized(data)
{
while(data.isEmpty()&&!endflag)
{
try {
data.wait();
} catch (InterruptedException e) {
throw new InterruptedIOException();
}
}
}
if(endflag)return -1;
else return data.remove(0);
}
}
Sorry for wasting so much space^^(and time :-().
It sounds as though the deliverable here is a subclass of ostream. The immediate question I'd want to be clear about is, will this class be responsible for buffering data until Java calls into it to retrieve, or is it expected to immediately (synchronously?) call via JNI to pass it on? That will be the strongest guide to how the code will shape up.
If you can reasonably expect the text to appear as a series of lines, I'd think about presenting them to Java in one line per call: this seems a fair compromise between the number of JNI calls and not unduly delaying the passing on of the text.
On the Java side I think you're looking at creating a Reader so that clients can pick up the text via a familiar interface, or perhaps a subclass of BufferedReader.