Scooter
Scooter

Reputation: 1079

Java ArrayList Multi-Threading NullPointerException

I've created a Glassfish 4.0 server in Java and I'm getting a strange error on my ArrayList<>. Here is my init() code:

@Override
public void contextInitialized(ServletContextEvent arg0) {
    init();
}

private void init() {
    sessions = new ArrayList<Session>();

    new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized(sessions){
                while(sessions.size() == 0){
                    try {
                        Thread.sleep(1000);
                        System.out.println("No sessions available: " + sessions);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
    }).start();
}

Essentially, when the server starts up the sessions ArrayList is instantiated, and a new Thread starts looping waiting for a WebSocket connection to occur. Here is the code for a WebSocket connection:

@OnOpen
public void open(Session session) {
    System.out.println("CONNECTED: " + session.getId());
    synchronized(sessions){
        sessions.add(session);
    }
}

So, the thread with the while loop should print out "No sessions available: []" until the open(...) method is called. However, whenever the code in the open method gets to synchronized(sessions) {...}, it says that sessions is null. This doesn't make sense considering that this method is NEVER called before the init() method that creates the sessions ArrayList.

So, what I did before was add in this code: if(sessions == null) sessions = new ArrayList<Session>(); and then the synchronized code would run. This would work, and the session would be added, but it wouldn't appear in the second thread. The second thread would still be looping and printing out that there are no sessions available. What am I missing?

Stacktrace:

2016-02-18T15:13:13.601-0500|Severe: java.lang.NullPointerException at BotManager.open(BotManager.java:134) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.glassfish.tyrus.core.AnnotatedEndpoint.callMethod(AnnotatedEndpoint.java:431) at org.glassfish.tyrus.core.AnnotatedEndpoint.onOpen(AnnotatedEndpoint.java:468) at org.glassfish.tyrus.core.EndpointWrapper.onConnect(EndpointWrapper.java:446) at org.glassfish.tyrus.server.TyrusEndpoint.onConnect(TyrusEndpoint.java:146) at org.glassfish.tyrus.websockets.DefaultWebSocket.onConnect(DefaultWebSocket.java:122) at org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler.init(TyrusHttpUpgradeHandler.java:98) at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:777) at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673) at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174) at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260) at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188) at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191) at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168) at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189) at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119) at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288) at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206) at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136) at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114) at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77) at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838) at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113) at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115) at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55) at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135) at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564) at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544) at java.lang.Thread.run(Thread.java:745)

Upvotes: 1

Views: 609

Answers (2)

wero
wero

Reputation: 33000

Your have a class which acts as ServletContextListener and a websocket EndPoint.

The container creates an instance which acts as ServletContextListener at application startup and creates Endpoint instances whenever a client connects.

Now each of these instances has an own sessions field and in the Endpoint it is still null when you access it (it is only initialized in the ServletContextListener instance).

Solution: make the sessions field static (and then resolve the deadlock issue - see comment of Andrew Williamson) - for instance simply use a synchronized list.

Upvotes: 4

erosb
erosb

Reputation: 3141

that this method is NEVER called before the init()

Possibly init() truly runs before open() , but the state change (list initialization) is not necessarily visible on an other thread where open() runs. The sessions list initalization is not in any synchronized block, therefore it may be invisible for other threads. You have the following options:

  • create the ArrayList instance in the class constructor (or at the field definition)
  • always syncronize on this (and not on sessions) when you access or create the session list (this will never be null)

Upvotes: 0

Related Questions