防止在页面加载时将后缀添加到资源中

时间:2022-11-11 22:20:51

I have a JSF2 application running and working no problem. The issue I am having with JSF is with the resource bundle. All resources have the .xhtml suffix appended to it. So main.css becomes main.css.xhtml when loaded in the browser. I would like to have it so the .xhtml isn't apended to the resources (don't mind about the pages themselves).

我有一个正在运行和工作的JSF2应用程序。我在JSF中遇到的问题是资源包。所有资源都添加了.xhtml后缀。那么主要。css变得main.css。在浏览器中加载xhtml。我希望它是这样的。xhtml并没有被分配到资源(不考虑页面本身)。

Is there a way where we can NOT have .xhtml appended to resources?

是否有一种方法可以不将.xhtml附加到参考资料中?

I would ideally not have to change the internal workings of the site. I have listed ideas below, but I have to say I don't really like these. Hoping for a solution somewhere?

理想情况下,我不需要更改站点的内部工作。我在下面列出了一些想法,但是我不得不说我不喜欢这些。希望在什么地方找到解决方案?

I am using Majorra v.2.1.17 on Glassfish 3.1.2.2.

我在Glassfish 3.1.2.2上使用的是马约拉v.2.1.17。

Current Faces Servlet loading as in web.xml (updated)

<servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/javax.faces.resource/*</url-pattern>
</servlet-mapping>

Why this questions is different from others

  • JSF 2 resources with CDN?. I am not looking to place my resources on a CDN, but to have my resources stay on my server but are pushed towards a CDN.
  • 使用CDN的JSF 2资源?我并不是想把我的资源放在CDN上,而是想让我的资源留在我的服务器上,但是被推到CDN上。
  • Change /javax.faces.resource prefix of resource URLs. I don't want to change the prefix. I want only to change the suffix. I would want <link type="text/css" rel="stylesheet" href="/javax.faces.resource/main03.css.xhtml?ln=styles"> to become : <link type="text/css" rel="stylesheet" href="/javax.faces.resource/main03.css?ln=styles"> WITHOUT the .xhtml extension.
  • 改变/ javax.faces。资源url的资源前缀。我不想改变前缀。我只想改变后缀。我想要
  • Changing JSF prefix to suffix mapping forces me to reapply the mapping on CSS background images. Since I have no issue with loading the resources. The site works, we are simply having a hard time differrentiating a webpage from a resource (Since we are looking at the extention alone).
  • 将JSF前缀更改为后缀映射迫使我对CSS背景图像重新应用映射。因为我对加载资源没有问题。这个网站运行良好,我们很难将一个网页从资源中分离出来(因为我们只关注扩展)。

Reasoning

Sure you might be asking me why I need this. Well, we are moving our application to be served by the Akamai CDN.

你肯定会问我为什么我需要这个。我们把我们的应用程序移到了Akamai CDN。

The issue we are having with the integration of the site is that we are trying to cache static content on the edge servers. This is done by matching file extensions (ie: .js, .doc, .png, css, etc). We cannot match xhtml because this would be caching all pages as well as static content. Which by that would cause problems with sessions and such.

我们在集成站点时遇到的问题是,我们试图在边缘服务器上缓存静态内容。这是通过匹配文件扩展名(例如:.js、.doc、.png、css等)来实现的。我们不能匹配xhtml,因为这将缓存所有页面和静态内容。这将会导致会议和这样的问题。

Attempted Solution

In line with the answer by BalusC, I have implemented the resource handler as suggested. I will not rewrite code here, since it is in answer below.

按照BalusC的答案,我实现了资源处理程序。我不会在这里重写代码,因为下面就是答案。

However, I am getting an error when loading composite components. I am getting an error as such :

但是,在加载复合组件时,我遇到了一个错误。我得到了这样一个错误:

WARNING: StandardWrapperValve[Faces Servlet]: PWC1406: Servlet.service() for servlet Faces Servlet threw exception
java.lang.NullPointerException
    at com.sun.faces.application.ApplicationImpl.createComponent(ApplicationImpl.java:975)
    at com.sun.faces.facelets.tag.jsf.CompositeComponentTagHandler.createComponent(CompositeComponentTagHandler.java:162)
    at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.createComponent(ComponentTagHandlerDelegateImpl.java:494)
    at com.sun.faces.facelets.tag.jsf.ComponentTagHandlerDelegateImpl.apply(ComponentTagHandlerDelegateImpl.java:169)
...

Composite component is loaded correctly because if I "unregister" the new ResourceHandler we just created it will load. The stack trace leads me to believe that it is trying to find this component in a java class, instead of finding it in the resources. According to grepcode this would be at this last line (975) where the error happens :

复合组件被正确地加载,因为如果我“取消注册”我们刚刚创建的新ResourceHandler,它将被加载。堆栈跟踪使我相信它试图在java类中找到这个组件,而不是在参考资料中找到它。根据grepcode,这将是发生错误的最后一行(975):

String packageName = componentResource.getLibraryName();
String className = componentResource.getResourceName();
className = packageName + '.' + className.substring(0, className.lastIndexOf('.'));

Meaning that the resourceName, aka className is null since the error I am getting is java.lang.NullPointerException. I can't seem to figure out how/where the ResourceHandler is called vis-a-vis a composite component. Any help figuring out this last issue?

因为我得到的错误是java.lang.NullPointerException,所以资源名(即className)为null。我似乎搞不清楚ResourceHandler是如何/在哪里被称为vi -a-vis -vis -vis -vis -vis -vis -vis组合组件的。能帮我弄清楚最后一个问题吗?

2 个解决方案

#1


13  

This is doable with a custom ResourceHandler which returns in createResource() a Resource which in turn returns an "unmapped" URL on Resource#getRequestPath(). You only need to add the default JSF resource prefix /javax.faces.resource/* to the <url-pattern> list of the FacesServlet mapping in order to get it to be triggered anyway.

可以使用定制的ResourceHandler返回createResource()资源,该资源反过来返回资源#getRequestPath()上的“未映射”URL。您只需添加默认的JSF资源前缀/java .faces。资源/*到FacesServlet映射的 列表,以便无论如何触发它。 模式>

Further, you need to override isResourceRequest() to check if the URL starts with the JSF resource prefix and also the handleResourceRequest() to locate and stream the proper resource.

此外,您需要重写isResourceRequest(),以检查URL是否以JSF资源前缀开头,以及handleResourceRequest()来定位和流适当的资源。

All with all, this should do:

总之,这应该是:

public class UnmappedResourceHandler extends ResourceHandlerWrapper {

    private ResourceHandler wrapped;

    public UnmappedResourceHandler(ResourceHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public Resource createResource(final String resourceName, final String libraryName) {
        final Resource resource = super.createResource(resourceName, libraryName);

        if (resource == null) {
            return null;
        }

        return new ResourceWrapper() {

            @Override
            public String getRequestPath() {
                ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
                String mapping = externalContext.getRequestServletPath();

                if (externalContext.getRequestPathInfo() == null) {
                    mapping = mapping.substring(mapping.lastIndexOf('.'));
                }

                String path = super.getRequestPath();

                if (mapping.charAt(0) == '/') {
                    return path.replaceFirst(mapping, "");
                }
                else if (path.contains("?")) {
                    return path.replace(mapping + "?", "?");
                }
                else {
                    return path.substring(0, path.length() - mapping.length());
                }
            }

            @Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
            public String getResourceName() {
                return resource.getResourceName();
            }

            @Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
            public String getLibraryName() {
                return resource.getLibraryName();
            }

            @Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
            public String getContentType() {
                return resource.getContentType();
            }

            @Override
            public Resource getWrapped() {
                return resource;
            }
        };
    }

    @Override
    public boolean isResourceRequest(FacesContext context) {
        return ResourceHandler.RESOURCE_IDENTIFIER.equals(context.getExternalContext().getRequestServletPath());
    }

    @Override
    public void handleResourceRequest(FacesContext context) throws IOException {
        ExternalContext externalContext = context.getExternalContext();
        String resourceName = externalContext.getRequestPathInfo();
        String libraryName = externalContext.getRequestParameterMap().get("ln");
        Resource resource = context.getApplication().getResourceHandler().createResource(resourceName, libraryName);

        if (resource == null) {
            super.handleResourceRequest(context);
            return;
        }

        if (!resource.userAgentNeedsUpdate(context)) {
            externalContext.setResponseStatus(HttpServletResponse.SC_NOT_MODIFIED);
            return;
        }

        externalContext.setResponseContentType(resource.getContentType());

        for (Entry<String, String> header : resource.getResponseHeaders().entrySet()) {
            externalContext.setResponseHeader(header.getKey(), header.getValue());
        }

        ReadableByteChannel input = null;
        WritableByteChannel output = null;

        try {
            input = Channels.newChannel(resource.getInputStream());
            output = Channels.newChannel(externalContext.getResponseOutputStream());

            for (ByteBuffer buffer = ByteBuffer.allocateDirect(10240); input.read(buffer) != -1; buffer.clear()) {
                output.write((ByteBuffer) buffer.flip());
            }
        }
        finally {
            if (output != null) try { output.close(); } catch (IOException ignore) {}
            if (input != null) try { input.close(); } catch (IOException ignore) {}
        }
    }

    @Override
    public ResourceHandler getWrapped() {
        return wrapped;
    }

}

Register it as follows in faces-config.xml:

在faces-config.xml中注册如下:

<application>
    <resource-handler>com.example.UnmappedResourceHandler</resource-handler>
</application>

Extend the FacesServlet URL pattern with ResourceHandler.RESOURCE_IDENTIFIER:

使用ResourceHandler.RESOURCE_IDENTIFIER:扩展FacesServlet URL模式:

<servlet-mapping>
    <servlet-name>facesServlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
    <url-pattern>/javax.faces.resource/*</url-pattern>
</servlet-mapping>

#2


3  

You could have a look at Rewrite. Rewrite allows to modify URLs that are rendered to the page and modify them in any way you want. You could do something like this to add a CDN To your site:

你可以看一下重写。重写允许修改呈现到页面的url并以任何您想要的方式修改它们。你可以做这样的事情,在你的网站上添加一个CDN:

.addRule(CDN.relocate("{p}foo-{version}.css")
         .where("p").matches(".*")
         .where("version").matches(".*")
         .to("http://mycdn.com/foo-{version}.css"));

I think it should be easy to implement your requirement using Rewrite.

我认为使用重写实现您的需求应该很容易。

Have a look at the example configurations to learn about the features of rewrite.

看一下示例配置,了解一下重写的特性。

#1


13  

This is doable with a custom ResourceHandler which returns in createResource() a Resource which in turn returns an "unmapped" URL on Resource#getRequestPath(). You only need to add the default JSF resource prefix /javax.faces.resource/* to the <url-pattern> list of the FacesServlet mapping in order to get it to be triggered anyway.

可以使用定制的ResourceHandler返回createResource()资源,该资源反过来返回资源#getRequestPath()上的“未映射”URL。您只需添加默认的JSF资源前缀/java .faces。资源/*到FacesServlet映射的 列表,以便无论如何触发它。 模式>

Further, you need to override isResourceRequest() to check if the URL starts with the JSF resource prefix and also the handleResourceRequest() to locate and stream the proper resource.

此外,您需要重写isResourceRequest(),以检查URL是否以JSF资源前缀开头,以及handleResourceRequest()来定位和流适当的资源。

All with all, this should do:

总之,这应该是:

public class UnmappedResourceHandler extends ResourceHandlerWrapper {

    private ResourceHandler wrapped;

    public UnmappedResourceHandler(ResourceHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public Resource createResource(final String resourceName, final String libraryName) {
        final Resource resource = super.createResource(resourceName, libraryName);

        if (resource == null) {
            return null;
        }

        return new ResourceWrapper() {

            @Override
            public String getRequestPath() {
                ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
                String mapping = externalContext.getRequestServletPath();

                if (externalContext.getRequestPathInfo() == null) {
                    mapping = mapping.substring(mapping.lastIndexOf('.'));
                }

                String path = super.getRequestPath();

                if (mapping.charAt(0) == '/') {
                    return path.replaceFirst(mapping, "");
                }
                else if (path.contains("?")) {
                    return path.replace(mapping + "?", "?");
                }
                else {
                    return path.substring(0, path.length() - mapping.length());
                }
            }

            @Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
            public String getResourceName() {
                return resource.getResourceName();
            }

            @Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
            public String getLibraryName() {
                return resource.getLibraryName();
            }

            @Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
            public String getContentType() {
                return resource.getContentType();
            }

            @Override
            public Resource getWrapped() {
                return resource;
            }
        };
    }

    @Override
    public boolean isResourceRequest(FacesContext context) {
        return ResourceHandler.RESOURCE_IDENTIFIER.equals(context.getExternalContext().getRequestServletPath());
    }

    @Override
    public void handleResourceRequest(FacesContext context) throws IOException {
        ExternalContext externalContext = context.getExternalContext();
        String resourceName = externalContext.getRequestPathInfo();
        String libraryName = externalContext.getRequestParameterMap().get("ln");
        Resource resource = context.getApplication().getResourceHandler().createResource(resourceName, libraryName);

        if (resource == null) {
            super.handleResourceRequest(context);
            return;
        }

        if (!resource.userAgentNeedsUpdate(context)) {
            externalContext.setResponseStatus(HttpServletResponse.SC_NOT_MODIFIED);
            return;
        }

        externalContext.setResponseContentType(resource.getContentType());

        for (Entry<String, String> header : resource.getResponseHeaders().entrySet()) {
            externalContext.setResponseHeader(header.getKey(), header.getValue());
        }

        ReadableByteChannel input = null;
        WritableByteChannel output = null;

        try {
            input = Channels.newChannel(resource.getInputStream());
            output = Channels.newChannel(externalContext.getResponseOutputStream());

            for (ByteBuffer buffer = ByteBuffer.allocateDirect(10240); input.read(buffer) != -1; buffer.clear()) {
                output.write((ByteBuffer) buffer.flip());
            }
        }
        finally {
            if (output != null) try { output.close(); } catch (IOException ignore) {}
            if (input != null) try { input.close(); } catch (IOException ignore) {}
        }
    }

    @Override
    public ResourceHandler getWrapped() {
        return wrapped;
    }

}

Register it as follows in faces-config.xml:

在faces-config.xml中注册如下:

<application>
    <resource-handler>com.example.UnmappedResourceHandler</resource-handler>
</application>

Extend the FacesServlet URL pattern with ResourceHandler.RESOURCE_IDENTIFIER:

使用ResourceHandler.RESOURCE_IDENTIFIER:扩展FacesServlet URL模式:

<servlet-mapping>
    <servlet-name>facesServlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
    <url-pattern>/javax.faces.resource/*</url-pattern>
</servlet-mapping>

#2


3  

You could have a look at Rewrite. Rewrite allows to modify URLs that are rendered to the page and modify them in any way you want. You could do something like this to add a CDN To your site:

你可以看一下重写。重写允许修改呈现到页面的url并以任何您想要的方式修改它们。你可以做这样的事情,在你的网站上添加一个CDN:

.addRule(CDN.relocate("{p}foo-{version}.css")
         .where("p").matches(".*")
         .where("version").matches(".*")
         .to("http://mycdn.com/foo-{version}.css"));

I think it should be easy to implement your requirement using Rewrite.

我认为使用重写实现您的需求应该很容易。

Have a look at the example configurations to learn about the features of rewrite.

看一下示例配置,了解一下重写的特性。