JRodd
JRodd

Reputation: 387

Netty ExceptionInInitializerError

I am into a IOT project and trying to sort out the learning curve and an odd angle of approach. I am a .Net programmer and it seems the go-to library is Netty which is Java based. Our client is already researching that to have it placed on their devices and insists on using HTTP instead of MQTT because they don't want firewall configuration to be a normal factor.

So, the first thing I should let you know is that I fist attempted to use DotNetty as a client and broker, but I couldn't make heads or tails of the library because it has no documentation and I couldn't tie the objects I was seeing in the Object Explorer to what I saw in the Netty documents. It then occurred to me that perhaps I could do something like I see in Xamarin where I am clearly accessing Java libraries from C# code. That led me to LKVM, which allowed me to convert the Netty jar file into a DLL file.

So, while I am using Visual Studio and C#, I am now able to follow the Netty tutorials and documents, which allowed for quite a bit of progress.

I wrote a little console app that is supposed to act like an IOT device with a Netty client on it. I used the DLL I produced to write a client. I got an error trying to connect and assumed that since I have no broker, it was just not finding an endpoint. So today I wrote the server code. Once I was done I tried to connect the device again and got the same error.

I then fired up Fiddler so I could check the traffic and saw that there was none. So the error is before any attempt to connect is made.

So, before I go any further, here is the client code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using JSON = Newtonsoft.Json.JsonConvert;
using io.netty.util;
using io.netty.bootstrap;
using io.netty.buffer;
using io.netty.channel;
using io.netty.channel.nio;
using io.netty.channel.socket;
using io.netty.channel.socket.nio;
using io.netty.handler.codec.http;
using io.netty.handler.codec.http.websocketx;
using io.netty.handler.codec.http.websocketx.extensions.compression;
using io.netty.handler.ssl;
using io.netty.handler.ssl.util;
using java.io;
using java.net;
using io.netty.util.concurrent;

namespace Device
{
    public class WebSocketClient
    {
        public string URL { get; set; }
        public URI Uri = null;
        public Uri netUri = null;
        public string Host;
        public int Port;
        public Channel Channel;
        public Bootstrap BootStrap = new Bootstrap();
        public WebSocketClientHandler Handler;
        public EventLoopGroup Group = new NioEventLoopGroup();
        public SslContext SslCtx = null;

        public WebSocketClient(string URL)
        {
            this.URL = URL;
            netUri = new Uri(this.URL);
            Uri = new URI(URL);
            Host = netUri.Host;
            Port = netUri.Port;//Unlike the Netty example, .Net knows the ws/wss ports.
            bool SSL = Port == 443;


            if (SSL)
            {
                SslCtx = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
            }

            Handler = new WebSocketClientHandler(WebSocketClientHandshakerFactory.newHandshaker(Uri, WebSocketVersion.V13, null, true, new DefaultHttpHeaders()));
            //BootStrap.group(Group).channel(new NioSocketChannel().GetType().handler(new ChannelInitializer(Handler, Host, Port, SslCtx))).connect(Host, Port).sync().channel();

        }

        public void Connect()
        {
            //Channel = BootStrap.connect(Host, Port).sync().channel();            
            //Channel = BootStrap.connect().sync().channel();
            //((Bootstrap)BootStrap.group(Group).channel(new NioSocketChannel().GetType()).handler(new ChannelInitializer(Handler, Host, Port, SslCtx))).connect(Host, Port).sync().channel();

            BootStrap = BootStrap.group(Group) as Bootstrap;
            BootStrap = BootStrap.channel(new NioSocketChannel().GetType()) as Bootstrap;
            BootStrap = BootStrap.handler(new ChannelInitializer(Handler, Host, Port, SslCtx)) as Bootstrap;            
            //BootStrap.remoteAddress(Host, Port);
            //BootStrap.localAddress(Host, Port);
            ChannelFuture cf = BootStrap.connect(Host, Port);
            cf = cf.sync();
            Channel = cf.channel();

            Handler.HandshakeFuture().sync();
        }

        public void Close()
        {
            Group.shutdownGracefully();
        }

        public void Ping()
        {
            WebSocketFrame frame = new PingWebSocketFrame(Unpooled.wrappedBuffer(new byte[] { 8, 1, 8, 1 }));
            Channel.writeAndFlush(frame);
        }

        public void Send(string content)
        {
            Channel.writeAndFlush(content);
            Channel.newPromise().addListener(new Listener());
        }

    }

    public class Listener : GenericFutureListener
    {
        public void operationComplete(Future f)
        {
            System.Console.WriteLine(JSON.SerializeObject(f.get()));
        }
    }
}

In the constructor there is a commented line that mimics what was in their client example. Because it was chained, it was hard to tell where the error was coming from. So, I moved that into my connect function and broke it out.

In the connect function, on the following line is where I see the first indication of trouble:

ChannelFuture cf = BootStrap.connect(Host, Port);

If I look at the object in my 'watch', I can see that is seems like an error is already present. It looks like this:

{DefaultChannelPromise@67755d(failure: java.lang.ExceptionInInitializerError)}

However, it does not error out on that line, but instead on the following line:

cf = cf.sync();

And the stack trace looks like this:

at io.netty.util.internal.PlatformDependent0.throwException(Exception )
   at io.netty.util.internal.PlatformDependent.throwException(Exception t)
   at io.netty.util.concurrent.DefaultPromise.rethrowIfFailed()
   at io.netty.util.concurrent.DefaultPromise.sync()
   at io.netty.channel.DefaultChannelPromise.sync()
   at io.netty.channel.DefaultChannelPromise.<bridge>sync()
   at io.netty.channel.DefaultChannelPromise.io.netty.channel.ChannelFuture/()Lio.netty.channel.ChannelFuture;sync()
   at Device.WebSocketClient.Connect() in c:\Users\ME\Documents\Visual Studio 2017\Projects\MQTT Sandbox\Device\WebSocketClient.cs:line 70
   at Device.Program.SocketTest() in c:\Users\ME\Documents\Visual Studio 2017\Projects\MQTT Sandbox\Device\Program.cs:line 106
   at Device.Program.Main(String[] args) in c:\Users\ME\Documents\Visual Studio 2017\Projects\MQTT Sandbox\Device\Program.cs:line 72

The only other lead I have is that if I explore that exception, there is an sub exception to the exception (as opposed to an inner exception) that says: "Unknown pointer size". That stack trace looks like this:

enter image description here

I have spent a good part of today tinkering and researching and I have come up with absolutely no leads as to what is wrong. Any help would be greatly appreciated.

Thanks in advance for any help!

Update I was able to extract a better stack trace from ChannelFuture cf = BootStrap.connect(Host, Port);

May 29, 2018 8:25:37 AM io.netty.channel.DefaultChannelId defaultProcessId
WARNING: Failed to find the current process ID from ''; using a random value: -251539282
May 29, 2018 8:25:43 AM io.netty.channel.AbstractChannel$AbstractUnsafe register
WARNING: Force-closing a channel whose registration task was not accepted by an event loop: [id: 0x6312b48a]
java.lang.ExceptionInInitializerError
        at io.netty.util.internal.shaded.org.jctools.queues.LinkedArrayQueueUtil.modifiedCalcElementOffset(LinkedArrayQueueUtil.java:24)
        at io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueue.offer(BaseMpscLinkedArrayQueue.java:308)
        at io.netty.util.concurrent.SingleThreadEventExecutor.offerTask(SingleThreadEventExecutor.java:330)
        at io.netty.util.concurrent.SingleThreadEventExecutor.addTask(SingleThreadEventExecutor.java:321)
        at io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:765)
        at io.netty.channel.AbstractChannel$AbstractUnsafe.register(AbstractChannel.java:479)
        at io.netty.channel.SingleThreadEventLoop.register(SingleThreadEventLoop.java:81)
        at io.netty.channel.SingleThreadEventLoop.register(SingleThreadEventLoop.java:74)
        at io.netty.channel.MultithreadEventLoopGroup.register(MultithreadEventLoopGroup.java:86)
        at io.netty.bootstrap.AbstractBootstrap.initAndRegister(AbstractBootstrap.java:333)
        at io.netty.bootstrap.Bootstrap.doResolveAndConnect(Bootstrap.java:163)
        at io.netty.bootstrap.Bootstrap.connect(Bootstrap.java:145)
        at io.netty.bootstrap.Bootstrap.connect(Bootstrap.java:126)
        at cli.Device.WebSocketClient.Connect(WebSocketClient.cs:70)
        at cli.Device.Program.SocketTest(Program.cs:106)
        at cli.Device.Program.Main(Program.cs:72)
Caused by: java.lang.IllegalStateException: Unknown pointer size
 at io.netty.util.internal.shaded.org.jctools.util.UnsafeRefArrayAccess.<clinit>(UnsafeRefArrayAccess.java:51)
        ... 11 more

May 29, 2018 8:25:43 AM io.netty.util.concurrent.DefaultPromise safeExecute
SEVERE: Failed to submit a listener notification task. Event loop shut down?
java.lang.NoClassDefFoundError: Could not initialize class io.netty.util.internal.shaded.org.jctools.util.UnsafeRefArrayAccess
        at io.netty.util.internal.shaded.org.jctools.queues.LinkedArrayQueueUtil.modifiedCalcElementOffset(LinkedArrayQueueUtil.java:24)
        at io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueue.offer(BaseMpscLinkedArrayQueue.java:308)
        at io.netty.util.concurrent.SingleThreadEventExecutor.offerTask(SingleThreadEventExecutor.java:330)
        at io.netty.util.concurrent.SingleThreadEventExecutor.addTask(SingleThreadEventExecutor.java:321)
        at io.netty.util.concurrent.SingleThreadEventExecutor.execute(SingleThreadEventExecutor.java:765)
        at io.netty.util.concurrent.DefaultPromise.safeExecute(DefaultPromise.java:764)
        at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:438)
        at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:104)
        at io.netty.channel.DefaultChannelPromise.trySuccess(DefaultChannelPromise.java:84)
        at io.netty.channel.AbstractChannel$CloseFuture.setClosed(AbstractChannel.java:1148)
        at io.netty.channel.AbstractChannel$AbstractUnsafe.register(AbstractChannel.java:491)
        at io.netty.channel.SingleThreadEventLoop.register(SingleThreadEventLoop.java:81)
        at io.netty.channel.SingleThreadEventLoop.register(SingleThreadEventLoop.java:74)
        at io.netty.channel.MultithreadEventLoopGroup.register(MultithreadEventLoopGroup.java:86)
        at io.netty.bootstrap.AbstractBootstrap.initAndRegister(AbstractBootstrap.java:333)
        at io.netty.bootstrap.Bootstrap.doResolveAndConnect(Bootstrap.java:163)
        at io.netty.bootstrap.Bootstrap.connect(Bootstrap.java:145)
        at io.netty.bootstrap.Bootstrap.connect(Bootstrap.java:126)
        at cli.Device.WebSocketClient.Connect(WebSocketClient.cs:70)
        at cli.Device.Program.SocketTest(Program.cs:106)
        at cli.Device.Program.Main(Program.cs:72)

Upvotes: 1

Views: 1462

Answers (1)

Kevin Langille
Kevin Langille

Reputation: 136

I have found a solution and implemented a fix in my branch of IKVM. https://github.com/Draspax/ikvm8

Basically in the class io.netty.util.internal.shaded.org.jctools.util.UnsafeRefArrayAccess there is a call to UnsafeAccess.UNSAFE.arrayIndexScale(Object[].class); which should return either 4 or 8 and the IKVM Unsafe implementation doesn't account for Object[].class so it returns the default 1. I have added the object array as an option for arrayIndexScale to return 4.

Hopefully will be included in https://github.com/wwrd/ikvm8 soon as these guys are keeping IKVM alive.

Upvotes: 1

Related Questions