如何删除静态资源的Orchestra converstation context参数?

时间:2012-06-07 12:21:47

标签: java jsf myfaces orchestra

为了提供正确的浏览器缓存,我想摆脱Apache MyFaces Orchestra为每个请求添加conversationContext参数,以获取对css文件的请求。

正如Bozho建议的那样,我已经实现了一个过滤器来设置Orchestra正在寻找的属性。

public class ResourceFilter implements Filter {

@Override
public void doFilter(ServletRequest request, ServletResponse theResponse, FilterChain theChain) throws IOException, ServletException {
    if(shouldNotAppendConversation(request)) {
        request.setAttribute(RequestParameterServletFilter.REQUEST_PARAM_FILTER_CALLED, Boolean.TRUE);
    }

    theChain.doFilter(request, theResponse);
}

private boolean shouldNotAppendConversation(ServletRequest theRequest) {
    HttpServletRequest aRequest = (HttpServletRequest) theRequest;
    String aPath = aRequest.getRequestURI();
    if(aPath.endsWith(".css.jsf")) {
        return true;
    }

    return false;
}

@Override
public void init(FilterConfig theFilterConfig) throws ServletException {
}

@Override
public void destroy() {
}
}

这不起作用参数仍附加到每个请求。在调试时,我发现过滤器首先受到对jsf站点的请求的影响。我确实希望在该请求中包含conversation context,因此过滤器会将请求直接转发到链中的下一个过滤器。命中过滤器的下一个请求(通常是对css文件的请求)已经包含在请求中的conversation context

奇怪的是,如果我将过滤器修改为始终设置属性,则所有请求都不会具有conversation context属性。但这意味着,conversation context也不包含在jsf网站的请求中(但应该)。

我注意到jsf网站生成的html中的css文件链接也包含conversation context属性,具体取决于过滤器实现。我想因为这个原因,第二个请求已经包含了conversation context参数?

我不明白为什么Orchestra会将conversation context参数附加到每个请求,而不仅仅是未设置属性的请求。

如何实现过滤器才能正常工作?

2 个答案:

答案 0 :(得分:3)

下一个请求(例如对于CSS文件)在请求到您的页面之后点击您的过滤器已经包含conversationContext参数只是因为这是该资源的URL已被页面呈现的方式先前的请求。

因此,应在渲染时对conversationContext进行控制。以下解决方案适用于JSF 2(我使用的是Mojarra 2.1.11,myfaces-orchestra-core20 1.5,RichFaces 4.1.0.Final)。一个特殊的servlet过滤器什么都不做,只用我们自己的包装器包装HttpServletResponse

public class RfOrchestraParamControlFilter implements Filter {
   ...
   @Override
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      response = new RfOrchestraParamControlResponseWrapper((HttpServletResponse)response);
      chain.doFilter(request, response);
   }
   ...
}

响应包装器测试要为being a richfaces resource编码的url,并在当前线程中打开Orchestra的ConversationRequestParameterProvider分离模式进行编码:

package ...
import javax.faces.application.ResourceHandler;
import org.richfaces.resource.ResourceHandlerImpl;
import org.apache.myfaces.orchestra.conversation.ConversationRequestParameterProvider;

public class RfOrchestraParamControlResponseWrapper extends HttpServletResponseWrapper {

   public RfOrchestraParamControlResponseWrapper(HttpServletResponse httpServletResponse) {
      super(httpServletResponse);
   }

   @Override
   public String encodeURL(String url) {
      if (url.contains(ResourceHandler.RESOURCE_IDENTIFIER) || url.contains(ResourceHandlerImpl.RICHFACES_RESOURCE_IDENTIFIER)) {
         boolean current = ConversationRequestParameterProvider.isInSeparationMode();
         /* Disable conversationContext parameter in current thread for the time of rendering link to a resource */
         ConversationRequestParameterProvider.setInSeparationMode(true);

         String result = super.encodeURL(url);

         /* Restore */
         ConversationRequestParameterProvider.setInSeparationMode(current);
         return result;
      }
      else return super.encodeURL(url);
   }

}

(在测试作为资源的url作为上下文路径时,我不得不使用String.contains()而不是String.startsWith(),并且servlet路径恰好在传递的url前面添加。)

然而,到目前为止,这无济于事。原因是Orchestra在其RequestParameterFacesContextFactory中使用自己的响应包装,并且在我们的过滤器被击中后发生。通过这种方式,Orchestra的包装结果证明是我们的包装器外部导致我们的包装器接收url为时已晚,当网址已被拦截且conversationContext附加。

为了避免这种情况,我们可以通过将RequestParameterFacesContextFactory拦截器的效果替换为实际does the same workRequestParameterServletFilter来使我们的响应包装器在Orchestra的外部。不幸的是,使用另一个过滤器并不是很精致我们可能没有,但到目前为止我还没有看到另一种方法。

所以,在web.xml中将过滤器放在 Orchestra之后:

<filter>
   <filter-name>requestParameterFilter</filter-name>
   <filter-class>org.apache.myfaces.orchestra.requestParameterProvider.RequestParameterServletFilter</filter-class>
</filter>
<filter>
   <filter-name>myOrchestraFilter</filter-name>
   <filter-class>mypkg.RfOrchestraParamControlFilter</filter-class>
</filter>

<filter-mapping>
   <filter-name>requestParameterFilter</filter-name>
   <servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
<filter-mapping>
   <filter-name>myOrchestraFilter</filter-name>
   <servlet-name>Faces Servlet</servlet-name>
</filter-mapping>

答案 1 :(得分:0)

无法使先前的解决方案起作用。我是这样做的:

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.List;
import java.util.stream.Collectors;

import javax.faces.application.ResourceHandler;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URIBuilder;

/**
 * @author Felipe Riccetto
 */
public class RemoveConversationParamFilter implements Filter {

    public static String removeQueryParameter(final String url,
            final String parameterName) throws URISyntaxException {
        URIBuilder uriBuilder = new URIBuilder(url);
        List<NameValuePair> queryParameters = uriBuilder.getQueryParams()
                .stream().filter(p -> !p.getName().equals(parameterName))
                .collect(Collectors.toList());
        if (queryParameters.isEmpty()) {
            uriBuilder.removeQuery();
        } else {
            uriBuilder.setParameters(queryParameters);
        }
        return uriBuilder.build().toString();
    }

    @Override
    public void destroy() {
        // nothing
    }

    @Override
    public void doFilter(final ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {

        HttpServletResponse resp2 = new HttpServletResponseWrapper(
                (HttpServletResponse) resp) {
            @Override
            public String encodeURL(String url) {

                String s = super.encodeURL(url);

                try {
                    String urlPath = new URL(
                            ((url.toLowerCase().startsWith("http://")
                                    || url.toLowerCase().startsWith("https://"))
                                            ? ""
                                            : "http://fake")
                                    + url).getPath().toString().toLowerCase();
                    if (urlPath.endsWith(".js") || urlPath.endsWith(".css")
                            || urlPath.endsWith(".png")
                            || urlPath.endsWith(".jpeg")
                            || urlPath.endsWith(".jpg")
                            || urlPath.endsWith(".gif") || urlPath.contains(
                                    ResourceHandler.RESOURCE_IDENTIFIER)) {
                        s = removeQueryParameter(s, "conversationContext");
                    }

                } catch (MalformedURLException | URISyntaxException e) {
                    // ignore
                }
                return s;
            }
        };

        chain.doFilter(req, resp2);
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
        // nothing
    }
}

web.xml:

<filter>
    <filter-name>RemoveConversationParamFilter</filter-name>
    <filter-class>RemoveConversationParamFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>RemoveConversationParamFilter</filter-name>
    <servlet-name>Faces Servlet</servlet-name>
</filter-mapping>

此最后一个映射必须是您的web.xml中的最后一个