SSE nullpointer in ChunkedOutput / OutputBuffer in a Jersey filter on Glassfish 4.1

I have a problem ( like this one ?) That started when I switched jersey (2.19) from descriptor deployment via @ApplicationPath to 2.x servlet in an existing JAX-RS web application. Once I finished SSE, it no longer worked. Since I used SSEBroadcaster at the time I built a simpler testing method, because I had no exceptions with the broadcaster.

Finally, an exception occurred:

Severe: java.lang.NullPointerException at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:350) at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:342) at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:161) at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:150) at org.glassfish.jersey.servlet.internal.ResponseWriter$NonCloseableOutputStreamWrapper.write(ResponseWriter.java:293) at org.glassfish.jersey.message.internal.CommittingOutputStream.write(CommittingOutputStream.java:214) at org.glassfish.jersey.media.sse.OutboundEventWriter.writeTo(OutboundEventWriter.java:100) at org.glassfish.jersey.media.sse.OutboundEventWriter.writeTo(OutboundEventWriter.java:63) at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:263) at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:250) at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162) at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1154) at org.glassfish.jersey.server.ChunkedOutput$1.call(ChunkedOutput.java:219) at org.glassfish.jersey.server.ChunkedOutput$1.call(ChunkedOutput.java:190) at org.glassfish.jersey.internal.Errors.process(Errors.java:315) at org.glassfish.jersey.internal.Errors.process(Errors.java:242) at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:347) at org.glassfish.jersey.server.ChunkedOutput.flushQueue(ChunkedOutput.java:190) at org.glassfish.jersey.server.ChunkedOutput.write(ChunkedOutput.java:180) at ch.company.app.controller.MyController$1.run(MyController.java:62) at java.lang.Thread.run(Thread.java:745) 

The code I used is a variant of the official Jersey documentation found here :

 @RequestScoped @Path("api") public class MyController { private final static Logger LOGGER = Logger.getLogger(MyController.class.getName()); @GET @Path("test") @Produces(SseFeature.SERVER_SENT_EVENTS) public EventOutput getServerSentEvents() { final EventOutput eventOutput = new EventOutput(); new Thread(new Runnable() { @Override public void run() { try { int counter = 0; while (!eventOutput.isClosed()) { LOGGER.log(Level.INFO, "Eventoutput closed?: " + eventOutput.isClosed()); final OutboundEvent.Builder eventBuilder = new OutboundEvent.Builder(); eventBuilder.name("message-to-client"); eventBuilder.id("c" + counter); eventBuilder.comment("this is a test comment"); eventBuilder.data(String.class, "Hello world " + counter + "!"); counter++; final OutboundEvent event = eventBuilder.build(); eventOutput.write(event); //Thread.sleep(Integer.toUnsignedLong(5000)); } } catch (IOException e) { throw new RuntimeException("Error when writing the event.", e); //} catch (InterruptedException ex) { // Logger.getLogger(MyController.class.getName()).log(Level.SEVERE, null, ex); } finally { try { eventOutput.close(); } catch (IOException ioClose) { throw new RuntimeException("Error when closing the event output.", ioClose); } } } }).start(); return eventOutput; } } 

If I use only eventBuilder.data(...) and do not use name(), id(), comment() , the exception (as suggested here ) would slightly change to this:

 Severe: java.lang.NullPointerException at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:350) at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:342) at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:161) at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:150) at org.glassfish.jersey.servlet.internal.ResponseWriter$NonCloseableOutputStreamWrapper.write(ResponseWriter.java:293) at org.glassfish.jersey.message.internal.CommittingOutputStream.write(CommittingOutputStream.java:214) at org.glassfish.jersey.media.sse.OutboundEventWriter$1.write(OutboundEventWriter.java:141) at java.io.OutputStream.write(OutputStream.java:116) at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221) at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291) at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:295) at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141) at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229) at java.io.BufferedWriter.flush(BufferedWriter.java:254) at org.glassfish.jersey.message.internal.ReaderWriter.writeToAsString(ReaderWriter.java:192) at org.glassfish.jersey.message.internal.AbstractMessageReaderWriterProvider.writeToAsString(AbstractMessageReaderWriterProvider.java:129) at org.glassfish.jersey.message.internal.StringMessageProvider.writeTo(StringMessageProvider.java:99) at org.glassfish.jersey.message.internal.StringMessageProvider.writeTo(StringMessageProvider.java:59) at org.glassfish.jersey.media.sse.OutboundEventWriter.writeTo(OutboundEventWriter.java:127) at org.glassfish.jersey.media.sse.OutboundEventWriter.writeTo(OutboundEventWriter.java:63) at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:263) at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:250) at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162) at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1154) at org.glassfish.jersey.server.ChunkedOutput$1.call(ChunkedOutput.java:219) at org.glassfish.jersey.server.ChunkedOutput$1.call(ChunkedOutput.java:190) at org.glassfish.jersey.internal.Errors.process(Errors.java:315) at org.glassfish.jersey.internal.Errors.process(Errors.java:242) at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:347) at org.glassfish.jersey.server.ChunkedOutput.flushQueue(ChunkedOutput.java:190) at org.glassfish.jersey.server.ChunkedOutput.write(ChunkedOutput.java:180) at ch.company.app.controller.MyController$1.run(MyController.java:62) at java.lang.Thread.run(Thread.java:745) 

The strange thing is that sometimes after restarting Glassfish (4.1) and redeploying when calling the method, I get the desired result from 1 to 90 times (without using Thread.sleep() β†’ why is it in the comment here), followed only by the NullPointerException. And that just does it, as far as I can tell.

Sample output before a NullPointerException exception:

 : this is a test comment event: message-to-client id: c0 data: Hello world 0! 

My web.xml regarding the container filter jersey 2.x servo:

 <filter> <filter-name>jersey</filter-name> <filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class> <init-param> <param-name>jersey.config.server.provider.packages</param-name> <param-value>ch.company.app</param-value> </init-param> <init-param> <param-name>jersey.config.servlet.filter.staticContentRegex</param-name> <param-value>^.+?\.(?:bmp|gif|png|jpg|jpeg|ico|css|js|pdf|txt|svg|eot|otf|ttf|woff|map)$</param-value> </init-param> </filter> <!-- <filter-mapping> <filter-name>jersey</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 

Since @ApplicationPath creates the Servlet 3.x container, I decided to try Servlet 3.x with the following web.xml instead of the Servlet 2.x filter, but it also leads me to the same exception:

 <servlet> <servlet-name>javax.ws.rs.core.Application</servlet-name> <init-param> <param-name>jersey.config.server.provider.packages</param-name> <param-value>ch.company.app</param-value> </init-param> <init-param> <param-name>jersey.config.server.provider.classnames</param-name> <param-value>org.glassfish.jersey.media.sse.SseFeature</param-value> </init-param> <init-param> <param-name>jersey.config.disableMoxyJson</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>jersey.config.servlet.filter.staticContentRegex</param-name> <param-value>^.+?\.(?:bmp|gif|png|jpg|jpeg|ico|css|js|pdf|txt|svg|eot|otf|ttf|woff|map)$</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>javax.ws.rs.core.Application</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> 

I even tried to add SseFeature manually (as suggested here: https://java.net/jira/browse/JERSEY-2150 ), which did not help anyone, nor on SSE, documentation is needed only until version 1.2.

 <init-param> <param-name>jersey.config.server.provider.classnames</param-name> <param-value>org.glassfish.jersey.media.sse.SseFeature</param-value> </init-param> 

So, now I spent two days to understand this, but I still can not understand. Any ideas?

Update

I realized that this only happens when Jersey is set to run as a filter. I managed to get it to work with Servlet 3.x and a web descriptor in the absence of initialization parameters that are not applicable when Jersey is configured as a servlet, as described here: https://jersey.java.net/apidocs/latest/jersey/org /glassfish/jersey/servlet/ServletProperties.html

 <servlet> <servlet-name>ch.company.app.ApplicationConfig</servlet-name> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>ch.company.app.ApplicationConfig</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>ch.company.app.ApplicationConfig</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> 

And the corresponding class

 public class ApplicationConfig extends Application { @Override public Set<Class<?>> getClasses() { Set<Class<?>> resources = new java.util.HashSet<>(); addRestResourceClasses(resources); return resources; } private void addRestResourceClasses(Set<Class<?>> resources) { resources.add(ch.company.app.controller.AppController.class); resources.add(ch.company.app.controller.IndexController.class); } } 

Unfortunately, I still cannot figure out how to access my static resources (css, js, images) using this approach, since FILTER_STATIC_CONTENT_REGEX is not available in the Servlet configuration. (I will investigate this separately.)

So the question is: why doesn't it work when Jersey is configured as a filter?

+5
source share

All Articles