AlreadyCommittedException:在执行 buildPage 之后但在从 PageInfo (HippoCMS) 写入响应之前,响应已经提交
AlreadyCommittedException: Response already committed after doing buildPage but before writing response from PageInfo (HippoCMS)
我们使用 REST 端点提供 CMS (Hippo) 内容,我注意到间歇性发生的奇怪行为:
在一段时间不活动或服务器重启后第一次调用端点(例如,http://site.name/api/articles/)时,它会一次又一次地抛出以下异常。如果第二次刷新页面,一切正常。
Throwable thrown during doFilter on request with URI: /site/fr-ca/api/articles/ and Query: nullResponse already committed after doing buildPage but before writing response from PageInfo.
[INFO] [talledLocalContainer] net.sf.ehcache.constructs.web.AlreadyCommittedException: Response already committed after doing buildPage but before writing response from PageInfo.
[INFO] [talledLocalContainer] at net.sf.ehcache.constructs.web.filter.CachingFilter.doFilter(CachingFilter.java:220)
[INFO] [talledLocalContainer] at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:86)
[INFO] [talledLocalContainer] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
[INFO] [talledLocalContainer] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
[INFO] [talledLocalContainer] at org.hippoecm.hst.container.XSSUrlFilter.doFilter(XSSUrlFilter.java:52)
[INFO] [talledLocalContainer] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
[INFO] [talledLocalContainer] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
[INFO] [talledLocalContainer] at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
[INFO] [talledLocalContainer] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
[INFO] [talledLocalContainer] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
[INFO] [talledLocalContainer] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
[INFO] [talledLocalContainer] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
[INFO] [talledLocalContainer] at org.apache.catalina.core.StandardContextValve.__invoke(StandardContextValve.java:106)
[INFO] [talledLocalContainer] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java)
[INFO] [talledLocalContainer] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
[INFO] [talledLocalContainer] at org.apache.catalina.core.StandardHostValve.__invoke(StandardHostValve.java:142)
[INFO] [talledLocalContainer] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java)
[INFO] [talledLocalContainer] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
[INFO] [talledLocalContainer] at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:617)
[INFO] [talledLocalContainer] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
[INFO] [talledLocalContainer] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
[INFO] [talledLocalContainer] at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
[INFO] [talledLocalContainer] at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
[INFO] [talledLocalContainer] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1521)
[INFO] [talledLocalContainer] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1478)
[INFO] [talledLocalContainer] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
[INFO] [talledLocalContainer] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
[INFO] [talledLocalContainer] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
[INFO] [talledLocalContainer] at java.lang.Thread.run(Thread.java:745)
下面给出了罪魁祸首代码:
protected void doFilter(final HttpServletRequest request,
final HttpServletResponse response, final FilterChain chain)
throws AlreadyGzippedException, AlreadyCommittedException,
FilterNonReentrantException, LockTimeoutException, Exception {
if (response.isCommitted()) {
throw new AlreadyCommittedException(
"Response already committed before doing buildPage.");
}
logRequestHeaders(request);
PageInfo pageInfo = buildPageInfo(request, response, chain);
if (pageInfo.isOk()) {
if (response.isCommitted()) {
throw new AlreadyCommittedException(
"Response already committed after doing buildPage"
+ " but before writing response from PageInfo.");
}
writeResponse(request, response, pageInfo);
}
}
第 220 行是在 if(response.isCommitted()) {}
、
中抛出 AlreadyCommittedException()
的行
这让我觉得服务器此时认为响应已经提交。我检查了我的代码,我可以确认我没有做任何 "in-between" 提交或写入 servlet 的输出流。
但最烦人的是,它只发生在第一个页面调用时,如果我刷新页面,问题就消失了。
其他详细信息:我们有一个缓存页面并调用 CacheFilter 的缓存过滤器:
public class WaPageCache extends SimpleCachingHeadersPageCachingFilter {
@Override
protected String calculateKey(HttpServletRequest httpRequest) {
String simpleKey = super.calculateKey(httpRequest);
final String requestURI = httpRequest.getRequestURI();
final String key = requestURI.contains("/binaries/content") ? simpleKey : simpleKey + httpRequest.getHeader("Cookie");
return key;
}
@Override
protected CacheManager getCacheManager() {
ClassLoader standardClassloader = Thread.currentThread().getContextClassLoader();
URL url = null;
if (standardClassloader != null) {
url = standardClassloader.getResource("ehcache-web-caching.xml");
}
return CacheManager.create(url);
}
}
有没有其他人遇到过类似的问题,有什么可能的causes/solutions/hints解决方案?
我找到了一个修复方法:在我的 web 应用程序中,我有几个过滤器,诀窍是将缓存过滤器放在所有其他过滤器之后(在我的 Hippo 案例中,特别是在 HstFilter 之后)。好吧,我有另一个设置响应 headers 的过滤器,并且可以在缓存过滤器之后使用。这是我更新后的片段 web.xml:
<filter-mapping>
<filter-name>HstFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- This is my filter that was throwing the spanner -->
<filter-mapping>
<filter-name>WaPageCache</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>ApiResourceHeadersFilter</filter-name>
<url-pattern>/api/site/*</url-pattern>
</filter-mapping>
我们使用 REST 端点提供 CMS (Hippo) 内容,我注意到间歇性发生的奇怪行为:
在一段时间不活动或服务器重启后第一次调用端点(例如,http://site.name/api/articles/)时,它会一次又一次地抛出以下异常。如果第二次刷新页面,一切正常。
Throwable thrown during doFilter on request with URI: /site/fr-ca/api/articles/ and Query: nullResponse already committed after doing buildPage but before writing response from PageInfo.
[INFO] [talledLocalContainer] net.sf.ehcache.constructs.web.AlreadyCommittedException: Response already committed after doing buildPage but before writing response from PageInfo.
[INFO] [talledLocalContainer] at net.sf.ehcache.constructs.web.filter.CachingFilter.doFilter(CachingFilter.java:220)
[INFO] [talledLocalContainer] at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:86)
[INFO] [talledLocalContainer] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
[INFO] [talledLocalContainer] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
[INFO] [talledLocalContainer] at org.hippoecm.hst.container.XSSUrlFilter.doFilter(XSSUrlFilter.java:52)
[INFO] [talledLocalContainer] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
[INFO] [talledLocalContainer] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
[INFO] [talledLocalContainer] at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
[INFO] [talledLocalContainer] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
[INFO] [talledLocalContainer] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
[INFO] [talledLocalContainer] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
[INFO] [talledLocalContainer] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
[INFO] [talledLocalContainer] at org.apache.catalina.core.StandardContextValve.__invoke(StandardContextValve.java:106)
[INFO] [talledLocalContainer] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java)
[INFO] [talledLocalContainer] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
[INFO] [talledLocalContainer] at org.apache.catalina.core.StandardHostValve.__invoke(StandardHostValve.java:142)
[INFO] [talledLocalContainer] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java)
[INFO] [talledLocalContainer] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
[INFO] [talledLocalContainer] at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:617)
[INFO] [talledLocalContainer] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
[INFO] [talledLocalContainer] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
[INFO] [talledLocalContainer] at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
[INFO] [talledLocalContainer] at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
[INFO] [talledLocalContainer] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1521)
[INFO] [talledLocalContainer] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1478)
[INFO] [talledLocalContainer] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
[INFO] [talledLocalContainer] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
[INFO] [talledLocalContainer] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
[INFO] [talledLocalContainer] at java.lang.Thread.run(Thread.java:745)
下面给出了罪魁祸首代码:
protected void doFilter(final HttpServletRequest request,
final HttpServletResponse response, final FilterChain chain)
throws AlreadyGzippedException, AlreadyCommittedException,
FilterNonReentrantException, LockTimeoutException, Exception {
if (response.isCommitted()) {
throw new AlreadyCommittedException(
"Response already committed before doing buildPage.");
}
logRequestHeaders(request);
PageInfo pageInfo = buildPageInfo(request, response, chain);
if (pageInfo.isOk()) {
if (response.isCommitted()) {
throw new AlreadyCommittedException(
"Response already committed after doing buildPage"
+ " but before writing response from PageInfo.");
}
writeResponse(request, response, pageInfo);
}
}
第 220 行是在 if(response.isCommitted()) {}
、
AlreadyCommittedException()
的行
这让我觉得服务器此时认为响应已经提交。我检查了我的代码,我可以确认我没有做任何 "in-between" 提交或写入 servlet 的输出流。
但最烦人的是,它只发生在第一个页面调用时,如果我刷新页面,问题就消失了。
其他详细信息:我们有一个缓存页面并调用 CacheFilter 的缓存过滤器:
public class WaPageCache extends SimpleCachingHeadersPageCachingFilter {
@Override
protected String calculateKey(HttpServletRequest httpRequest) {
String simpleKey = super.calculateKey(httpRequest);
final String requestURI = httpRequest.getRequestURI();
final String key = requestURI.contains("/binaries/content") ? simpleKey : simpleKey + httpRequest.getHeader("Cookie");
return key;
}
@Override
protected CacheManager getCacheManager() {
ClassLoader standardClassloader = Thread.currentThread().getContextClassLoader();
URL url = null;
if (standardClassloader != null) {
url = standardClassloader.getResource("ehcache-web-caching.xml");
}
return CacheManager.create(url);
}
}
有没有其他人遇到过类似的问题,有什么可能的causes/solutions/hints解决方案?
我找到了一个修复方法:在我的 web 应用程序中,我有几个过滤器,诀窍是将缓存过滤器放在所有其他过滤器之后(在我的 Hippo 案例中,特别是在 HstFilter 之后)。好吧,我有另一个设置响应 headers 的过滤器,并且可以在缓存过滤器之后使用。这是我更新后的片段 web.xml:
<filter-mapping>
<filter-name>HstFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- This is my filter that was throwing the spanner -->
<filter-mapping>
<filter-name>WaPageCache</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>ApiResourceHeadersFilter</filter-name>
<url-pattern>/api/site/*</url-pattern>
</filter-mapping>