Project: springxd
Issue XD-1670: NPE when a container departs
When a container departs the cluster the admin will try to redeploy any modules that container was running. If the stream was *destroyed* and the container exited before it had the chance to clean up its deployments under {{/xd/deployments/modules}} (for example, with {{kill -9}}) the following NPE occurs: {noformat} java.lang.NullPointerException org.springframework.xd.dirt.server.ContainerListener.loadStream(ContainerListener.java:347) org.springframework.xd.dirt.server.ContainerListener.onChildLeft(ContainerListener.java:403) org.springframework.xd.dirt.server.ContainerListener.childEvent(ContainerListener.java:158) org.apache.curator.framework.recipes.cache.PathChildrenCache$5.apply(PathChildrenCache.java:494) org.apache.curator.framework.recipes.cache.PathChildrenCache$5.apply(PathChildrenCache.java:488) org.apache.curator.framework.listen.ListenerContainer$1.run(ListenerContainer.java:92) com.google.common.util.concurrent.MoreExecutors$SameThreadExecutorService.execute(MoreExecutors.java:293) org.apache.curator.framework.listen.ListenerContainer.forEach(ListenerContainer.java:83) org.apache.curator.framework.recipes.cache.PathChildrenCache.callListeners(PathChildrenCache.java:485) org.apache.curator.framework.recipes.cache.EventOperation.invoke(EventOperation.java:35) org.apache.curator.framework.recipes.cache.PathChildrenCache$11.run(PathChildrenCache.java:755) java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) java.util.concurrent.FutureTask.run(FutureTask.java:262) java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) java.util.concurrent.FutureTask.run(FutureTask.java:262) java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) java.lang.Thread.run(Thread.java:724) {noformat} If the stream was *undeployed* the following stack appears: {noformat} 15:13:06,002 ERROR ContainersPathChildrenCache-0 cache.PathChildrenCache:550 - java.lang.RuntimeException: org.apache.zookeeper.KeeperException$NoNodeException: KeeperErrorCode = NoNode for /xd/deployments/streams/t0 org.springframework.xd.dirt.server.ContainerListener.onChildLeft(ContainerListener.java:468) org.springframework.xd.dirt.server.ContainerListener.childEvent(ContainerListener.java:159) org.apache.curator.framework.recipes.cache.PathChildrenCache$5.apply(PathChildrenCache.java:494) org.apache.curator.framework.recipes.cache.PathChildrenCache$5.apply(PathChildrenCache.java:488) org.apache.curator.framework.listen.ListenerContainer$1.run(ListenerContainer.java:92) com.google.common.util.concurrent.MoreExecutors$SameThreadExecutorService.execute(MoreExecutors.java:293) org.apache.curator.framework.listen.ListenerContainer.forEach(ListenerContainer.java:83) org.apache.curator.framework.recipes.cache.PathChildrenCache.callListeners(PathChildrenCache.java:485) org.apache.curator.framework.recipes.cache.EventOperation.invoke(EventOperation.java:35) org.apache.curator.framework.recipes.cache.PathChildrenCache$11.run(PathChildrenCache.java:755) java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) java.util.concurrent.FutureTask.run(FutureTask.java:266) java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) java.util.concurrent.FutureTask.run(FutureTask.java:266) java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) java.lang.Thread.run(Thread.java:744) Caused by: org.apache.zookeeper.KeeperException$NoNodeException: KeeperErrorCode = NoNode for /xd/deployments/streams/t0 org.apache.zookeeper.KeeperException.create(KeeperException.java:111) org.apache.zookeeper.KeeperException.create(KeeperException.java:51) org.apache.zookeeper.ZooKeeper.getData(ZooKeeper.java:1155) org.apache.curator.framework.imps.GetDataBuilderImpl$4.call(GetDataBuilderImpl.java:302) org.apache.curator.framework.imps.GetDataBuilderImpl$4.call(GetDataBuilderImpl.java:291) org.apache.curator.RetryLoop.callWithRetry(RetryLoop.java:107) org.apache.curator.framework.imps.GetDataBuilderImpl.pathInForeground(GetDataBuilderImpl.java:287) org.apache.curator.framework.imps.GetDataBuilderImpl.forPath(GetDataBuilderImpl.java:279) org.apache.curator.framework.imps.GetDataBuilderImpl.forPath(GetDataBuilderImpl.java:41) org.springframework.xd.dirt.server.ContainerListener.loadStream(ContainerListener.java:358) org.springframework.xd.dirt.server.ContainerListener.onChildLeft(ContainerListener.java:417)	... 16 more {noformat} In short, this logic makes the assumption that the stream is still present and deployed. It needs to take into account the fact that neither assumption can be made.
Story points: 2