Reputation: 1079
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
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
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:
ArrayList
instance in the class constructor (or at the field definition)this
(and not on sessions
) when you access or create the session list (this
will never be null)Upvotes: 0