diff --git a/transport-native-epoll/src/main/c/exception_helper.h b/transport-native-epoll/src/main/c/exception_helper.h index d8c77078f5..f2f079876c 100644 --- a/transport-native-epoll/src/main/c/exception_helper.h +++ b/transport-native-epoll/src/main/c/exception_helper.h @@ -16,6 +16,7 @@ void throwRuntimeException(JNIEnv* env, char* message); void throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber); +void throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber); void throwIOException(JNIEnv* env, char* message); void throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber); void throwClosedChannelException(JNIEnv* env); diff --git a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c index 9ba001aace..cc1915fefa 100644 --- a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c +++ b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c @@ -86,6 +86,7 @@ jfieldID packetCountFieldId = NULL; jmethodID inetSocketAddrMethodId = NULL; jmethodID datagramSocketAddrMethodId = NULL; jclass runtimeExceptionClass = NULL; +jclass channelExceptionClass = NULL; jclass ioExceptionClass = NULL; jclass closedChannelExceptionClass = NULL; jmethodID closedChannelExceptionMethodId = NULL; @@ -109,6 +110,13 @@ void throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { free(allocatedMessage); } +void throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { + char* allocatedMessage = exceptionMessage(message, errorNumber); + (*env)->ThrowNew(env, channelExceptionClass, allocatedMessage); + free(allocatedMessage); +} + + void throwIOException(JNIEnv* env, char* message) { (*env)->ThrowNew(env, ioExceptionClass, message); } @@ -163,7 +171,7 @@ static inline jint getOption(JNIEnv* env, jint fd, int level, int optname, void* return 0; } int err = errno; - throwRuntimeExceptionErrorNo(env, "getsockopt() failed: ", err); + throwChannelExceptionErrorNo(env, "getsockopt() failed: ", err); return code; } @@ -171,7 +179,7 @@ static inline int setOption(JNIEnv* env, jint fd, int level, int optname, const int rc = setsockopt(fd, level, optname, optval, len); if (rc < 0) { int err = errno; - throwRuntimeExceptionErrorNo(env, "setsockopt() failed: ", err); + throwChannelExceptionErrorNo(env, "setsockopt() failed: ", err); } return rc; } @@ -392,6 +400,18 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { return JNI_ERR; } + jclass localChannelExceptionClass = (*env)->FindClass(env, "io/netty/channel/ChannelException"); + if (localChannelExceptionClass == NULL) { + // pending exception... + return JNI_ERR; + } + channelExceptionClass = (jclass) (*env)->NewGlobalRef(env, localChannelExceptionClass); + if (channelExceptionClass == NULL) { + // out-of-memory! + throwOutOfMemoryError(env); + return JNI_ERR; + } + jclass localNetUtilClass = (*env)->FindClass(env, "io/netty/util/NetUtil" ); if (localNetUtilClass == NULL) { // pending exception... @@ -619,6 +639,9 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) { if (runtimeExceptionClass != NULL) { (*env)->DeleteGlobalRef(env, runtimeExceptionClass); } + if (channelExceptionClass != NULL) { + (*env)->DeleteGlobalRef(env, channelExceptionClass); + } if (ioExceptionClass != NULL) { (*env)->DeleteGlobalRef(env, ioExceptionClass); } @@ -642,7 +665,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_eventFd(JNIEnv* env, j if (eventFD < 0) { int err = errno; - throwRuntimeExceptionErrorNo(env, "eventfd() failed: ", err); + throwChannelExceptionErrorNo(env, "eventfd() failed: ", err); } return eventFD; } @@ -652,7 +675,7 @@ JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_eventFdWrite(JNIEnv* e if (eventFD < 0) { int err = errno; - throwRuntimeExceptionErrorNo(env, "eventfd_write() failed: ", err); + throwChannelExceptionErrorNo(env, "eventfd_write() failed: ", err); } } @@ -676,9 +699,9 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollCreate(JNIEnv* en if (efd < 0) { int err = errno; if (epoll_create1) { - throwRuntimeExceptionErrorNo(env, "epoll_create1() failed: ", err); + throwChannelExceptionErrorNo(env, "epoll_create1() failed: ", err); } else { - throwRuntimeExceptionErrorNo(env, "epoll_create() failed: ", err); + throwChannelExceptionErrorNo(env, "epoll_create() failed: ", err); } return efd; } @@ -686,7 +709,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollCreate(JNIEnv* en if (fcntl(efd, F_SETFD, FD_CLOEXEC) < 0) { int err = errno; close(efd); - throwRuntimeExceptionErrorNo(env, "fcntl() failed: ", err); + throwChannelExceptionErrorNo(env, "fcntl() failed: ", err); return err; } } diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollChannelConfigTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollChannelConfigTest.java new file mode 100644 index 0000000000..218891f5de --- /dev/null +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollChannelConfigTest.java @@ -0,0 +1,52 @@ +/* + * Copyright 2015 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. + */ +package io.netty.channel.epoll; + +import io.netty.channel.ChannelException; +import org.junit.Test; + +import static org.junit.Assert.fail; + +public class EpollChannelConfigTest { + + @Test + public void testOptionGetThrowsChannelException() throws Exception { + Epoll.ensureAvailability(); + EpollSocketChannel channel = new EpollSocketChannel(); + channel.config().getSoLinger(); + channel.fd().close(); + try { + channel.config().getSoLinger(); + fail(); + } catch (ChannelException e) { + // expected + } + } + + @Test + public void testOptionSetThrowsChannelException() throws Exception { + Epoll.ensureAvailability(); + EpollSocketChannel channel = new EpollSocketChannel(); + channel.config().setKeepAlive(true); + channel.fd().close(); + try { + channel.config().setKeepAlive(true); + fail(); + } catch (ChannelException e) { + // expected + } + } +}