Native Transport Netty Class Package Prefix

Motivation:
transport-native-epoll finds java classes from JNI using fully qualified class names. If a shaded version of Netty is used then these lookups will fail.

Modifications:
- Allow a prefix to be appended to Netty class names in JNI code.

Result:
JNI code can be used with shaded version of Netty.
This commit is contained in:
Scott Mitchell 2016-01-14 22:58:38 -08:00
parent bf24ffd335
commit dea337b4cf
9 changed files with 136 additions and 27 deletions

View File

@ -38,6 +38,7 @@
#include "netty_unix_filedescriptor.h"
#include "netty_unix_socket.h"
#include "netty_unix_errors.h"
#include "netty_unix_util.h"
// TCP_NOTSENT_LOWAT is defined in linux 3.12. We define this here so older kernels can compile.
#ifndef TCP_NOTSENT_LOWAT
@ -84,6 +85,9 @@ jfieldID packetScopeIdFieldId = NULL;
jfieldID packetPortFieldId = NULL;
jfieldID packetMemoryAddressFieldId = NULL;
jfieldID packetCountFieldId = NULL;
static jstring nettyPackagePrefixJString = NULL;
static const char* nettyPackagePrefix = NULL;
static char* nettyClassName = NULL;
// util methods
static int getSysctlValue(const char * property, int* returnValue) {
@ -116,17 +120,35 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
} else {
if (netty_unix_errors_JNI_OnLoad(env) == JNI_ERR) {
// Load the prefix to use when looking for Netty classes
jclass systemCls = (*env)->FindClass(env, "java/lang/System");
if (systemCls == NULL) {
return JNI_ERR;
}
if (netty_unix_filedescriptor_JNI_OnLoad(env) == JNI_ERR) {
jmethodID getPropertyMethod = (*env)->GetStaticMethodID(env, systemCls, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
if (getPropertyMethod == NULL) {
return JNI_ERR;
}
if (netty_unix_socket_JNI_OnLoad(env) == JNI_ERR) {
jstring propertyName = (*env)->NewStringUTF(env, "io.netty.native.epoll.nettyPackagePrefix");
nettyPackagePrefixJString = (*env)->CallStaticObjectMethod(env, systemCls, getPropertyMethod, propertyName);
if (nettyPackagePrefixJString != NULL) {
nettyPackagePrefix = (*env)->GetStringUTFChars(env, nettyPackagePrefixJString, 0);
}
if (netty_unix_errors_JNI_OnLoad(env, nettyPackagePrefix) == JNI_ERR) {
return JNI_ERR;
}
if (netty_unix_filedescriptor_JNI_OnLoad(env, nettyPackagePrefix) == JNI_ERR) {
return JNI_ERR;
}
if (netty_unix_socket_JNI_OnLoad(env, nettyPackagePrefix) == JNI_ERR) {
return JNI_ERR;
}
jclass fileRegionCls = (*env)->FindClass(env, "io/netty/channel/DefaultFileRegion");
nettyClassName = netty_unix_util_prepend(nettyPackagePrefix, "io/netty/channel/DefaultFileRegion");
jclass fileRegionCls = (*env)->FindClass(env, nettyClassName);
free(nettyClassName);
nettyClassName = NULL;
if (fileRegionCls == NULL) {
// pending exception...
return JNI_ERR;
@ -164,7 +186,10 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
return JNI_ERR;
}
jclass nativeDatagramPacketCls = (*env)->FindClass(env, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket");
nettyClassName = netty_unix_util_prepend(nettyPackagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket");
jclass nativeDatagramPacketCls = (*env)->FindClass(env, nettyClassName);
free(nettyClassName);
nettyClassName = NULL;
if (nativeDatagramPacketCls == NULL) {
// pending exception...
return JNI_ERR;
@ -197,6 +222,12 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
return JNI_ERR;
}
if (nettyPackagePrefixJString != NULL) {
(*env)->ReleaseStringUTFChars(env, nettyPackagePrefixJString, nettyPackagePrefix);
nettyPackagePrefix = NULL;
nettyPackagePrefixJString = NULL;
}
return JNI_VERSION_1_6;
}
}
@ -208,6 +239,15 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) {
return;
} else {
// delete global references so the GC can collect them
if (nettyPackagePrefixJString != NULL) {
(*env)->ReleaseStringUTFChars(env, nettyPackagePrefixJString, nettyPackagePrefix);
nettyPackagePrefix = NULL;
nettyPackagePrefixJString = NULL;
}
if (nettyClassName != NULL) {
free(nettyClassName);
nettyClassName = NULL;
}
netty_unix_errors_JNI_OnUnLoad(env);
netty_unix_filedescriptor_JNI_OnUnLoad(env);
netty_unix_socket_JNI_OnUnLoad(env);

View File

@ -17,6 +17,7 @@
#include <stdlib.h>
#include <errno.h>
#include "netty_unix_errors.h"
#include "netty_unix_util.h"
#include "io_netty_channel_unix_Errors.h"
static jclass runtimeExceptionClass = NULL;
@ -24,22 +25,14 @@ static jclass channelExceptionClass = NULL;
static jclass ioExceptionClass = NULL;
static jclass closedChannelExceptionClass = NULL;
static jmethodID closedChannelExceptionMethodId = NULL;
static char* nettyClassName = NULL;
/** Notice: every usage of exceptionMessage needs to release the allocated memory for the sequence of char */
static char* exceptionMessage(char* msg, int error) {
if (error < 0) {
// some functions return negative values
// and it's hard to keep track of when to send -error and when not
// this will just take care when things are forgotten
// what would generate a proper error
error = error * -1;
}
//strerror is returning a constant, so no need to free anything coming from strerror
char* err = strerror(error);
char* result = malloc(strlen(msg) + strlen(err) + 1);
strcpy(result, msg);
strcat(result, err);
return result;
// strerror is returning a constant, so no need to free anything coming from strerror
// error may be negative because some functions return negative values. we should make sure it is always
// positive when passing to standard library functions.
return netty_unix_util_prepend(msg, strerror(error < 0 ? -error : error));
}
// Exported C methods
@ -79,7 +72,7 @@ void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env) {
(*env)->ThrowNew(env, exceptionClass, "");
}
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env) {
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix) {
jclass localRuntimeExceptionClass = (*env)->FindClass(env, "java/lang/RuntimeException");
if (localRuntimeExceptionClass == NULL) {
// pending exception...
@ -92,7 +85,10 @@ jint netty_unix_errors_JNI_OnLoad(JNIEnv* env) {
return JNI_ERR;
}
jclass localChannelExceptionClass = (*env)->FindClass(env, "io/netty/channel/ChannelException");
nettyClassName = netty_unix_util_prepend(nettyPackagePrefix, "io/netty/channel/ChannelException");
jclass localChannelExceptionClass = (*env)->FindClass(env, nettyClassName);
free(nettyClassName);
nettyClassName = NULL;
if (localChannelExceptionClass == NULL) {
// pending exception...
return JNI_ERR;
@ -139,6 +135,10 @@ jint netty_unix_errors_JNI_OnLoad(JNIEnv* env) {
void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env) {
// delete global references so the GC can collect them
if (nettyClassName != NULL) {
free(nettyClassName);
nettyClassName = NULL;
}
if (runtimeExceptionClass != NULL) {
(*env)->DeleteGlobalRef(env, runtimeExceptionClass);
runtimeExceptionClass = NULL;

View File

@ -74,7 +74,7 @@ static jint _read(JNIEnv* env, jclass clazz, jint fd, void* buffer, jint pos, ji
return (jint) res;
}
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env) {
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix) {
void* mem = malloc(1);
if (mem == NULL) {
netty_unix_errors_throwOutOfMemoryError(env);

View File

@ -26,6 +26,7 @@
#include "netty_unix_errors.h"
#include "netty_unix_socket.h"
#include "netty_unix_util.h"
#include "io_netty_channel_unix_Socket.h"
static jclass datagramSocketAddressClass = NULL;
@ -35,6 +36,7 @@ static jclass inetSocketAddressClass = NULL;
static jclass netUtilClass = NULL;
static jmethodID netUtilClassIpv4PreferredMethodId = NULL;
static int socketType;
static char* nettyClassName = NULL;
static const char* ip4prefix = "::ffff:";
// Optional external methods
@ -301,8 +303,11 @@ int netty_unix_socket_setOption(JNIEnv* env, jint fd, int level, int optname, co
return rc;
}
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env) {
jclass localDatagramSocketAddressClass = (*env)->FindClass(env, "io/netty/channel/unix/DatagramSocketAddress");
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix) {
nettyClassName = netty_unix_util_prepend(nettyPackagePrefix, "io/netty/channel/unix/DatagramSocketAddress");
jclass localDatagramSocketAddressClass = (*env)->FindClass(env, nettyClassName);
free(nettyClassName);
nettyClassName = NULL;
if (localDatagramSocketAddressClass == NULL) {
// pending exception...
return JNI_ERR;
@ -334,7 +339,10 @@ jint netty_unix_socket_JNI_OnLoad(JNIEnv* env) {
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: InetSocketAddress.<init>(String, int)");
return JNI_ERR;
}
jclass localNetUtilClass = (*env)->FindClass(env, "io/netty/util/NetUtil" );
nettyClassName = netty_unix_util_prepend(nettyPackagePrefix, "io/netty/util/NetUtil");
jclass localNetUtilClass = (*env)->FindClass(env, nettyClassName);
free(nettyClassName);
nettyClassName = NULL;
if (localNetUtilClass == NULL) {
// pending exception...
return JNI_ERR;
@ -376,6 +384,10 @@ jint netty_unix_socket_JNI_OnLoad(JNIEnv* env) {
}
void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) {
if (nettyClassName != NULL) {
free(nettyClassName);
nettyClassName = NULL;
}
if (datagramSocketAddressClass != NULL) {
(*env)->DeleteGlobalRef(env, datagramSocketAddressClass);
datagramSocketAddressClass = NULL;

View File

@ -27,7 +27,7 @@ void netty_unix_errors_throwClosedChannelException(JNIEnv* env);
void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env);
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env);
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix);
void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env);
#endif /* NETTY_UNIX_ERRORS_H_ */

View File

@ -19,7 +19,7 @@
#include <jni.h>
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env);
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix);
void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env);
#endif /* NETTY_UNIX_FILEDESCRIPTOR_H_ */

View File

@ -25,7 +25,7 @@ int netty_unix_socket_getOption(JNIEnv* env, jint fd, int level, int optname, vo
int netty_unix_socket_setOption(JNIEnv* env, jint fd, int level, int optname, const void* optval, socklen_t len);
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env);
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* nettyPackagePrefix);
void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env);
#endif /* NETTY_UNIX_SOCKET_H_ */

View File

@ -0,0 +1,30 @@
/*
* Copyright 2016 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#include <string.h>
#include "netty_unix_util.h"
char* netty_unix_util_prepend(const char* prefix, const char* str) {
if (prefix == NULL) {
char* result = (char*) malloc(sizeof(char) * (strlen(str) + 1));
strcpy(result, str);
return result;
}
char* result = (char*) malloc(sizeof(char) * (strlen(prefix) + strlen(str) + 1));
strcpy(result, prefix);
strcat(result, str);
return result;
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2016 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
#ifndef NETTY_UNIX_UTIL_H_
#define NETTY_UNIX_UTIL_H_
/**
* Return a new string (caller must free this string) which is equivalent to <pre>prefix + str</pre>.
*
* Caller must free the return value!
*/
char* netty_unix_util_prepend(const char* prefix, const char* str);
#endif /* NETTY_UNIX_UTIL_H_ */