java: Split up large Java extension into smaller C files.

This commit is contained in:
Richard W.M. Jones
2016-09-02 22:25:50 +01:00
parent 63db9ae81f
commit 76eee79a65
8 changed files with 331 additions and 252 deletions

2
.gitignore vendored
View File

@@ -280,9 +280,9 @@ Makefile.in
/inspector/virt-inspector.1
/installcheck.sh
/install-sh
/java/actions-?.c
/java/api
/java/Bindtests.java
/java/com_redhat_et_libguestfs_GuestFS.c
/java/com_redhat_et_libguestfs_GuestFS.h
/java/com/redhat/et/libguestfs/GuestFS.java
/java/doc-stamp

View File

@@ -199,7 +199,14 @@ fuse/test-guestmount-fd.c
fuse/test-guestunmount-fd.c
get-kernel/dummy.c
inspector/inspector.c
java/com_redhat_et_libguestfs_GuestFS.c
java/actions-0.c
java/actions-1.c
java/actions-2.c
java/actions-3.c
java/actions-4.c
java/actions-5.c
java/actions-6.c
java/handle.c
lua/lua-guestfs.c
make-fs/make-fs.c
mllib/dev_t-c.c

View File

@@ -566,7 +566,7 @@ public class %s {
pr "}\n"
and generate_java_c () =
and generate_java_c actions () =
generate_header CStyle LGPLv2plus;
pr "\
@@ -581,20 +581,6 @@ and generate_java_c () =
#include \"guestfs.h\"
#include \"guestfs-internal-frontend.h\"
/* This is the opaque data passed between _set_event_callback and
* the C wrapper which calls the Java event callback.
*
* NB: The 'callback' in the following struct is registered as a global
* reference. It must be freed along with the struct.
*/
struct callback_data {
JavaVM *jvm; // JVM
jobject callback; // object supporting EventCallback interface
jmethodID method; // callback.event method
};
static struct callback_data **get_all_event_callbacks (JNIEnv *env, guestfs_h *g, size_t *len_rtn);
/* Note that this function returns. The exception is not thrown
* until after the wrapper function returns.
*/
@@ -618,241 +604,12 @@ throw_out_of_memory (JNIEnv *env, const char *msg)
\"com/redhat/et/libguestfs/LibGuestFSOutOfMemory\");
(*env)->ThrowNew (env, cl, msg);
}
JNIEXPORT jlong JNICALL
Java_com_redhat_et_libguestfs_GuestFS__1create (JNIEnv *env,
jobject obj_unused, jint flags)
{
guestfs_h *g;
g = guestfs_create_flags ((int) flags);
if (g == NULL) {
throw_exception (env, \"GuestFS.create: failed to allocate handle\");
return 0;
}
guestfs_set_error_handler (g, NULL, NULL);
return (jlong) (long) g;
}
JNIEXPORT void JNICALL
Java_com_redhat_et_libguestfs_GuestFS__1close
(JNIEnv *env, jobject obj, jlong jg)
{
guestfs_h *g = (guestfs_h *) (long) jg;
size_t len, i;
struct callback_data **data;
/* There is a nasty, difficult to solve case here where the
* user deletes events in one of the callbacks that we are
* about to invoke, resulting in a double-free. XXX
*/
data = get_all_event_callbacks (env, g, &len);
guestfs_close (g);
for (i = 0; i < len; ++i) {
(*env)->DeleteGlobalRef (env, data[i]->callback);
free (data[i]);
}
free (data);
}
/* See EventCallback interface. */
#define METHOD_NAME \"event\"
#define METHOD_SIGNATURE \"(JILjava/lang/String;[J)V\"
static void
java_callback (guestfs_h *g,
void *opaque,
uint64_t event,
int event_handle,
int flags,
const char *buf, size_t buf_len,
const uint64_t *array, size_t array_len)
{
struct callback_data *data = opaque;
JavaVM *jvm = data->jvm;
JNIEnv *env;
int r;
jstring jbuf;
jlongArray jarray;
size_t i;
jlong jl;
/* Get the Java environment. See:
* http://stackoverflow.com/questions/12900695/how-to-obtain-jni-interface-pointer-jnienv-for-asynchronous-calls
*/
r = (*jvm)->GetEnv (jvm, (void **) &env, JNI_VERSION_1_6);
if (r != JNI_OK) {
switch (r) {
case JNI_EDETACHED:
/* This can happen when the close event is generated during an atexit
* cleanup. The JVM has probably been destroyed so I doubt it is
* safe to run Java code at this point.
*/
fprintf (stderr, \"%%s: event %%\" PRIu64 \" (eh %%d) ignored because the thread is not attached to the JVM. This can happen when libguestfs handles are cleaned up at program exit after the JVM has been destroyed.\\n\",
__func__, event, event_handle);
return;
case JNI_EVERSION:
fprintf (stderr, \"%%s: event %%\" PRIu64 \" (eh %%d) failed because the JVM version is too old. JVM >= 1.6 is required.\\n\",
__func__, event, event_handle);
return;
default:
fprintf (stderr, \"%%s: jvm->GetEnv failed! (JNI_* error code = %%d)\\n\",
__func__, r);
return;
}
}
/* Convert the buffer and array to Java objects. */
jbuf = (*env)->NewStringUTF (env, buf); // XXX size
jarray = (*env)->NewLongArray (env, array_len);
for (i = 0; i < array_len; ++i) {
jl = array[i];
(*env)->SetLongArrayRegion (env, jarray, i, 1, &jl);
}
/* Call the event method. If it throws an exception, all we can do is
* print it on stderr.
*/
(*env)->ExceptionClear (env);
(*env)->CallVoidMethod (env, data->callback, data->method,
(jlong) event, (jint) event_handle,
jbuf, jarray);
if ((*env)->ExceptionOccurred (env)) {
(*env)->ExceptionDescribe (env);
(*env)->ExceptionClear (env);
}
}
JNIEXPORT jint JNICALL
Java_com_redhat_et_libguestfs_GuestFS__1set_1event_1callback
(JNIEnv *env, jobject obj, jlong jg, jobject jcallback, jlong jevents)
{
guestfs_h *g = (guestfs_h *) (long) jg;
int r;
struct callback_data *data;
jclass callback_class;
jmethodID method;
char key[64];
callback_class = (*env)->GetObjectClass (env, jcallback);
method = (*env)->GetMethodID (env, callback_class, METHOD_NAME, METHOD_SIGNATURE);
if (method == 0) {
throw_exception (env, \"GuestFS.set_event_callback: callback class does not implement the EventCallback interface\");
return -1;
}
data = malloc (sizeof *data);
if (data == NULL) {
throw_out_of_memory (env, \"malloc\");
return -1;
}
(*env)->GetJavaVM (env, &data->jvm);
data->method = method;
r = guestfs_set_event_callback (g, java_callback,
(uint64_t) jevents, 0, data);
if (r == -1) {
free (data);
throw_exception (env, guestfs_last_error (g));
return -1;
}
/* Register jcallback as a global reference so the GC won't free it. */
data->callback = (*env)->NewGlobalRef (env, jcallback);
/* Store 'data' in the handle, so we can free it at some point. */
snprintf (key, sizeof key, \"_java_event_%%d\", r);
guestfs_set_private (g, key, data);
return (jint) r;
}
JNIEXPORT void JNICALL
Java_com_redhat_et_libguestfs_GuestFS__1delete_1event_1callback
(JNIEnv *env, jobject obj, jlong jg, jint eh)
{
guestfs_h *g = (guestfs_h *) (long) jg;
char key[64];
struct callback_data *data;
snprintf (key, sizeof key, \"_java_event_%%d\", eh);
data = guestfs_get_private (g, key);
if (data) {
(*env)->DeleteGlobalRef (env, data->callback);
free (data);
guestfs_set_private (g, key, NULL);
guestfs_delete_event_callback (g, eh);
}
}
JNIEXPORT jstring JNICALL
Java_com_redhat_et_libguestfs_GuestFS__1event_1to_1string
(JNIEnv *env, jclass cl, jlong jevents)
{
uint64_t events = (uint64_t) jevents;
char *str;
jstring jr;
str = guestfs_event_to_string (events);
if (str == NULL) {
perror (\"guestfs_event_to_string\");
return NULL;
}
jr = (*env)->NewStringUTF (env, str);
free (str);
return jr;
}
static struct callback_data **
get_all_event_callbacks (JNIEnv *env, guestfs_h *g, size_t *len_rtn)
{
struct callback_data **r;
size_t i;
const char *key;
struct callback_data *data;
/* Count the length of the array that will be needed. */
*len_rtn = 0;
data = guestfs_first_private (g, &key);
while (data != NULL) {
if (strncmp (key, \"_java_event_\", strlen (\"_java_event_\")) == 0)
(*len_rtn)++;
data = guestfs_next_private (g, &key);
}
/* Copy them into the return array. */
r = malloc (sizeof (struct callback_data *) * (*len_rtn));
if (r == NULL) {
throw_out_of_memory (env, \"malloc\");
return NULL;
}
i = 0;
data = guestfs_first_private (g, &key);
while (data != NULL) {
if (strncmp (key, \"_java_event_\", strlen (\"_java_event_\")) == 0) {
r[i] = data;
i++;
}
data = guestfs_next_private (g, &key);
}
return r;
}
";
List.iter (
fun { name = name; style = (ret, args, optargs as style);
c_function = c_function } ->
pr "\n";
pr "JNIEXPORT ";
(match ret with
| RErr -> pr "void ";

View File

@@ -16,7 +16,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*)
val generate_java_c : unit -> unit
val generate_java_c : Types.action list -> unit -> unit
val generate_java_gitignore : unit -> unit
val generate_java_java : unit -> unit
val generate_java_makefile_inc : unit -> unit

View File

@@ -174,7 +174,7 @@ Run it from the top source directory using the command
"java/com/redhat/et/libguestfs/*.java";
output_to "java/Makefile.inc" generate_java_makefile_inc;
output_to "java/com_redhat_et_libguestfs_GuestFS.c" generate_java_c;
output_to_subset "java/actions-%d.c" generate_java_c;
output_to "java/com/redhat/et/libguestfs/.gitignore" generate_java_gitignore;
output_to "java/Bindtests.java" generate_java_bindtests;
output_to "haskell/Guestfs.hs" generate_haskell_hs;

View File

@@ -22,7 +22,13 @@ java_prefix = com/redhat/et/libguestfs
generator_built = \
Makefile.inc \
$(java_built_sources) \
com_redhat_et_libguestfs_GuestFS.c \
actions-0.c \
actions-1.c \
actions-2.c \
actions-3.c \
actions-4.c \
actions-5.c \
actions-6.c \
$(srcdir)/Bindtests.java
# Pull in automatically generated built sources
@@ -91,7 +97,14 @@ jnilibdir = $(JNI_INSTALL_DIR)
libguestfs_jni_la_SOURCES = \
com_redhat_et_libguestfs_GuestFS.h \
com_redhat_et_libguestfs_GuestFS.c
actions-0.c \
actions-1.c \
actions-2.c \
actions-3.c \
actions-4.c \
actions-5.c \
actions-6.c \
handle.c
libguestfs_jni_la_CPPFLAGS = \
-DGUESTFS_PRIVATE=1 \

295
java/handle.c Normal file
View File

@@ -0,0 +1,295 @@
/* libguestfs Java bindings.
* Copyright (C) 2009-2016 Red Hat Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "com_redhat_et_libguestfs_GuestFS.h"
#include "guestfs.h"
#include "guestfs-internal-frontend.h"
/* This is the opaque data passed between _set_event_callback and
* the C wrapper which calls the Java event callback.
*
* NB: The 'callback' in the following struct is registered as a global
* reference. It must be freed along with the struct.
*/
struct callback_data {
JavaVM *jvm; // JVM
jobject callback; // object supporting EventCallback interface
jmethodID method; // callback.event method
};
static struct callback_data **get_all_event_callbacks (JNIEnv *env, guestfs_h *g, size_t *len_rtn);
/* Note that this function returns. The exception is not thrown
* until after the wrapper function returns.
*/
static void
throw_exception (JNIEnv *env, const char *msg)
{
jclass cl;
cl = (*env)->FindClass (env,
"com/redhat/et/libguestfs/LibGuestFSException");
(*env)->ThrowNew (env, cl, msg);
}
/* Note that this function returns. The exception is not thrown
* until after the wrapper function returns.
*/
static void
throw_out_of_memory (JNIEnv *env, const char *msg)
{
jclass cl;
cl = (*env)->FindClass (env,
"com/redhat/et/libguestfs/LibGuestFSOutOfMemory");
(*env)->ThrowNew (env, cl, msg);
}
JNIEXPORT jlong JNICALL
Java_com_redhat_et_libguestfs_GuestFS__1create (JNIEnv *env,
jobject obj_unused, jint flags)
{
guestfs_h *g;
g = guestfs_create_flags ((int) flags);
if (g == NULL) {
throw_exception (env, "GuestFS.create: failed to allocate handle");
return 0;
}
guestfs_set_error_handler (g, NULL, NULL);
return (jlong) (long) g;
}
JNIEXPORT void JNICALL
Java_com_redhat_et_libguestfs_GuestFS__1close
(JNIEnv *env, jobject obj, jlong jg)
{
guestfs_h *g = (guestfs_h *) (long) jg;
size_t len, i;
struct callback_data **data;
/* There is a nasty, difficult to solve case here where the
* user deletes events in one of the callbacks that we are
* about to invoke, resulting in a double-free. XXX
*/
data = get_all_event_callbacks (env, g, &len);
guestfs_close (g);
for (i = 0; i < len; ++i) {
(*env)->DeleteGlobalRef (env, data[i]->callback);
free (data[i]);
}
free (data);
}
/* See EventCallback interface. */
#define METHOD_NAME "event"
#define METHOD_SIGNATURE "(JILjava/lang/String;[J)V"
static void
java_callback (guestfs_h *g,
void *opaque,
uint64_t event,
int event_handle,
int flags,
const char *buf, size_t buf_len,
const uint64_t *array, size_t array_len)
{
struct callback_data *data = opaque;
JavaVM *jvm = data->jvm;
JNIEnv *env;
int r;
jstring jbuf;
jlongArray jarray;
size_t i;
jlong jl;
/* Get the Java environment. See:
* http://stackoverflow.com/questions/12900695/how-to-obtain-jni-interface-pointer-jnienv-for-asynchronous-calls
*/
r = (*jvm)->GetEnv (jvm, (void **) &env, JNI_VERSION_1_6);
if (r != JNI_OK) {
switch (r) {
case JNI_EDETACHED:
/* This can happen when the close event is generated during an atexit
* cleanup. The JVM has probably been destroyed so I doubt it is
* safe to run Java code at this point.
*/
fprintf (stderr, "%s: event %" PRIu64 " (eh %d) ignored because the thread is not attached to the JVM. This can happen when libguestfs handles are cleaned up at program exit after the JVM has been destroyed.\n",
__func__, event, event_handle);
return;
case JNI_EVERSION:
fprintf (stderr, "%s: event %" PRIu64 " (eh %d) failed because the JVM version is too old. JVM >= 1.6 is required.\n",
__func__, event, event_handle);
return;
default:
fprintf (stderr, "%s: jvm->GetEnv failed! (JNI_* error code = %d)\n",
__func__, r);
return;
}
}
/* Convert the buffer and array to Java objects. */
jbuf = (*env)->NewStringUTF (env, buf); // XXX size
jarray = (*env)->NewLongArray (env, array_len);
for (i = 0; i < array_len; ++i) {
jl = array[i];
(*env)->SetLongArrayRegion (env, jarray, i, 1, &jl);
}
/* Call the event method. If it throws an exception, all we can do is
* print it on stderr.
*/
(*env)->ExceptionClear (env);
(*env)->CallVoidMethod (env, data->callback, data->method,
(jlong) event, (jint) event_handle,
jbuf, jarray);
if ((*env)->ExceptionOccurred (env)) {
(*env)->ExceptionDescribe (env);
(*env)->ExceptionClear (env);
}
}
JNIEXPORT jint JNICALL
Java_com_redhat_et_libguestfs_GuestFS__1set_1event_1callback
(JNIEnv *env, jobject obj, jlong jg, jobject jcallback, jlong jevents)
{
guestfs_h *g = (guestfs_h *) (long) jg;
int r;
struct callback_data *data;
jclass callback_class;
jmethodID method;
char key[64];
callback_class = (*env)->GetObjectClass (env, jcallback);
method = (*env)->GetMethodID (env, callback_class, METHOD_NAME, METHOD_SIGNATURE);
if (method == 0) {
throw_exception (env, "GuestFS.set_event_callback: callback class does not implement the EventCallback interface");
return -1;
}
data = malloc (sizeof *data);
if (data == NULL) {
throw_out_of_memory (env, "malloc");
return -1;
}
(*env)->GetJavaVM (env, &data->jvm);
data->method = method;
r = guestfs_set_event_callback (g, java_callback,
(uint64_t) jevents, 0, data);
if (r == -1) {
free (data);
throw_exception (env, guestfs_last_error (g));
return -1;
}
/* Register jcallback as a global reference so the GC won't free it. */
data->callback = (*env)->NewGlobalRef (env, jcallback);
/* Store 'data' in the handle, so we can free it at some point. */
snprintf (key, sizeof key, "_java_event_%d", r);
guestfs_set_private (g, key, data);
return (jint) r;
}
JNIEXPORT void JNICALL
Java_com_redhat_et_libguestfs_GuestFS__1delete_1event_1callback
(JNIEnv *env, jobject obj, jlong jg, jint eh)
{
guestfs_h *g = (guestfs_h *) (long) jg;
char key[64];
struct callback_data *data;
snprintf (key, sizeof key, "_java_event_%d", eh);
data = guestfs_get_private (g, key);
if (data) {
(*env)->DeleteGlobalRef (env, data->callback);
free (data);
guestfs_set_private (g, key, NULL);
guestfs_delete_event_callback (g, eh);
}
}
JNIEXPORT jstring JNICALL
Java_com_redhat_et_libguestfs_GuestFS__1event_1to_1string
(JNIEnv *env, jclass cl, jlong jevents)
{
uint64_t events = (uint64_t) jevents;
char *str;
jstring jr;
str = guestfs_event_to_string (events);
if (str == NULL) {
perror ("guestfs_event_to_string");
return NULL;
}
jr = (*env)->NewStringUTF (env, str);
free (str);
return jr;
}
static struct callback_data **
get_all_event_callbacks (JNIEnv *env, guestfs_h *g, size_t *len_rtn)
{
struct callback_data **r;
size_t i;
const char *key;
struct callback_data *data;
/* Count the length of the array that will be needed. */
*len_rtn = 0;
data = guestfs_first_private (g, &key);
while (data != NULL) {
if (strncmp (key, "_java_event_", strlen ("_java_event_")) == 0)
(*len_rtn)++;
data = guestfs_next_private (g, &key);
}
/* Copy them into the return array. */
r = malloc (sizeof (struct callback_data *) * (*len_rtn));
if (r == NULL) {
throw_out_of_memory (env, "malloc");
return NULL;
}
i = 0;
data = guestfs_first_private (g, &key);
while (data != NULL) {
if (strncmp (key, "_java_event_", strlen ("_java_event_")) == 0) {
r[i] = data;
i++;
}
data = guestfs_next_private (g, &key);
}
return r;
}

View File

@@ -294,7 +294,14 @@ gobject/src/struct-xattr.c
gobject/src/struct-xfsinfo.c
gobject/src/tristate.c
inspector/inspector.c
java/com_redhat_et_libguestfs_GuestFS.c
java/actions-0.c
java/actions-1.c
java/actions-2.c
java/actions-3.c
java/actions-4.c
java/actions-5.c
java/actions-6.c
java/handle.c
lua/lua-guestfs.c
make-fs/make-fs.c
mllib/dev_t-c.c