1

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)

8
  • How is sessions defined? Commented Feb 18, 2016 at 20:26
  • Global variable ArrayList<Session> sessions; Commented Feb 18, 2016 at 20:26
  • Could you please post the stack trace of your NullPointerException? Commented Feb 18, 2016 at 20:26
  • @ScottyPippen you should show the whole class including declaration of the session member Commented Feb 18, 2016 at 20:27
  • 3
    You have a deadlock issue with your synchronization - your runnable locks the list until the size is greater than 0, and you can't add to the list until it is unlocked... Commented Feb 18, 2016 at 20:30

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Done! This is the correct solution! Thanks, I never would have guessed that there were multiple underlying instances of the same variables :)
0

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)

8 Comments

synchronized(this){ sessions = new ArrayList<Session>(); } is that what you're proposing in the init() method?
yes. And replace every existing syncronized(sessions) to syncronized(this)
if I remember correctly, setting sessions as volatile should work as well.
volatile can fix the initialization problem too, but it doesn't guarantee the proper behavior of the ArrayList itself. Furthermore, using both volatile and synchronized may cause performance problems (not totally sure about that)
I'm still getting a NullPointer on the initialization even after making it volatile
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.