Websocket CdiUtils NPE с использованием <f: websocket />
Это приложение отлично работало в TC7, Spring 3 и JSF 2.2, но я хотел добавить возможности push-уведомлений в JSF 2.3, поэтому после некоторых простых начальных проблем я обновился до TC 9.0.8, Spring 5.0.5 и JSF 2.3.0. из-за обновлений приложение развертывается нормально и Spring Security работает (я могу войти), но затем я получаю эту ошибку:
May 11, 2018 2:24:06 PM com.sun.faces.application.view.FaceletViewHandlingStrategy handleRenderException
SEVERE: Error Rendering View[/index.xhtml]
java.lang.NullPointerException
at com.sun.faces.cdi.CdiUtils.getBeanReferenceByType(CdiUtils.java:230)
at com.sun.faces.cdi.CdiUtils.getBeanReference(CdiUtils.java:213)
at com.sun.faces.renderkit.html_basic.WebsocketRenderer.encodeEnd(WebsocketRenderer.java:111)
at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:949)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1912)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1908)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1908)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:491)
at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:194)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:151)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:126)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:223)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:671)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:494)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:651)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:412)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:754)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1385)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Мой отладчик показывает, что на CdiUtils:230 beanManager имеет значение null, и я подозреваю, что не могу подтвердить, что FacesContext имеет значение null на CdiUtils:213. Я подозреваю, что это происходит из-за путаницы из-за наличия нескольких контейнеров (Tomcat, CDI, Spring и JSF) одновременно. Мне нравятся Spring Security, RestTemplate и JdbcTemplate, поэтому я немного не хочу отказываться от Spring. Вот мой пуш-бин. Я бросил каждую аннотацию на это у меня было:
@Component
@Scope("Application")
public class PushBean implements Serializable {
private static final long serialVersionUID = -6558314691176100417L;
@Inject
@Push(channel = "facilitatorBroadcastMessages")
private PushContext push;
private String facilitatorBroadcastMessage;
public void setFacilitatorBroadcastMessage(String message) {
this.facilitatorBroadcastMessage = message;
}
public void broadcastFacilitatorMessage() {
push.send(facilitatorBroadcastMessage);
}
}
Код Facelet, который отправляет сообщение:
<p:button value="Send Broadcast Message to All Users" onclick="PF('broadcastMessageDialog').show(); " />
<p:dialog id="broadcastMessageDialog" header="Facilitator Message" widgetVar="broadcastMessageDialog" height="500px">
<p:inputText id="facilitatorBroadcastMessageInput" value="#{pushBean.facilitatorBroadcastMessage}" />
<p:commandButton onclick="PF('broadcastMessageDialog').hide(); " value="Send" process="@parent" action="#{pushBean.broadcastFacilitatorMessage()}" />
</p:dialog>
JS слушатель websocket:
function websocketListener(message, channel, event) {
$("#facilitatorBroadcastMessage").innerHTML = message;
PF('facilitatorBroadcastMessageDialog').show();
}
Тег веб-сокета JSF и код для отображения сообщения:
<f:websocket channel="facilitatorBroadcastMessages" onmessage="webSocketListener" />
<p:dialog id="facilitatorBroadcastMessageDialog" header="Facilitator Message" widgetVar="facilitatorBroadcastMessageDialog" height="500px">
<p id="facilitatorBroadcastMessage"></p>
<p:button onclick="PF('facilitatorBroadcastMessageDialog').hide(); " value="Got it, thanks!"/>
</p:dialog>
Любые идеи, как это исправить?
1 ответ
Посмотрите на этот http://balusc.omnifaces.org/2013/10/how-to-install-cdi-in-tomcat.html. Вам необходимо установить Weld.
Вам нужно сделать это три шага:
- Удалите файл weld-servlet-shaded.jar в веб-приложении /WEB-INF/lib. Если вы используете Maven, это координата:
<dependency>
<groupId>org.jboss.weld.servlet</groupId>
<artifactId>weld-servlet-shaded</artifactId>
<version>3.0.3.Final</version>
</dependency>
- Создайте файл /META-INF/context.xml в веб-контенте веб-приложения со следующим содержимым (или, если он у вас есть, добавьте в него только запись):
<Context>
<Resource name="BeanManager"
auth="Container"
type="javax.enterprise.inject.spi.BeanManager"
factory="org.jboss.weld.resources.ManagerObjectFactory" />
</Context>
Это зарегистрирует фабрику BeanManager компании Weld в JNDI компании Tomcat. Это не может быть выполнено программным способом Weld, потому что JNDI Tomcat предназначен только для чтения. Этот шаг не обязателен, если вы нацелены как минимум на Mojarra 2.2.11 и / или OmniFaces 2.4 или новее. Оба могут найти его в ServletContext. Тем не менее, могут быть другие библиотеки, которые все еще ожидают найти BeanManager в JNDI, тогда лучше всего сохранить этот файл конфигурации для этих библиотек.
- Создайте (пустой) файл /WEB-INF/beans.xml (нет, не в /META-INF! Это только для внутренних файлов JAR, таких как OmniFaces).