从流中包含包含JSF标签/组件的动态内容

4
我正在开发一个应用程序,希望从流中包含动态XHTML内容。为了处理这个问题,我编写了一个标签处理器扩展,将动态XHTML内容转储到输出组件中。
UIOutput htmlChild = (UIOutput) ctx.getFacesContext().getApplication().createComponent(UIOutput.COMPONENT_TYPE);
htmlChild.setValue(new String(outputStream.toByteArray(), "utf-8"));

对于没有JSF标签的XHTML内容,这个方法可以正常工作。但是,如果在我的动态XHTML内容中有JSF标签,比如<h:inputText value="#{bean.item}"/>,它们会被打印成纯文本。我希望它们能够呈现为输入框。我该如何实现这一点?


这是一个由我的应用程序工具填充的 ByteArrayOutputStream。该 ByteArrayOutputStream 包含生成的 XHTML 内容。 - user3027786
编写JSP自定义标签库是否有帮助?据我所知,它们位于JSF生命周期的顶部。 - user3027786
@BalusC,基于临时文件的Resourcehandler机制是否适用于<ui:include ../>?通常资源处理程序是用于静态资源的。我在这里看不到您建议的代码片段了。 - user3027786
当然可以工作,否则我不会将其发布为答案。除非明确说明,我从不发布未经测试的代码。我删除它是因为根据评论,这种方法不被您接受,而且我无法在没有事先进行调查/测试的情况下立即提供一个完整的、可行的、非临时文件的方法(但从理论上讲,这肯定是可能的)。 - BalusC
你能提供基于临时文件的解决方案吗?我希望在未来几天内至少有一个可行的解决方案。也许稍后有人可以提供一个动态解决方案的建议。 - user3027786
我恢复了答案。 - BalusC
2个回答

5
基本上,你应该使用<ui:include>与自定义的ResourceHandler结合使用,能够以URL的形式返回资源。所以当你有一个OutputStream时,你应该把它写入一个(临时)文件中,这样你就可以从中获取一个URL

例如:

<ui:include src="/dynamic.xhtml" />

使用

public class DynamicResourceHandler extends ResourceHandlerWrapper {

    private ResourceHandler wrapped;

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

    @Override
    public ViewResource createViewResource(FacesContext context, String resourceName) {
        if (resourceName.equals("/dynamic.xhtml")) {
            try {
                File file = File.createTempFile("dynamic-", ".xhtml");

                try (Writer writer = new FileWriter(file)) {
                    writer
                        .append("<ui:composition")
                        .append(" xmlns:ui='http://java.sun.com/jsf/facelets'")
                        .append(" xmlns:h='http://java.sun.com/jsf/html'")
                        .append(">")
                        .append("<p>Hello from a dynamic include!</p>")
                        .append("<p>The below should render as a real input field:</p>")
                        .append("<p><h:inputText /></p>")
                        .append("</ui:composition>");
                }

                final URL url = file.toURI().toURL();
                return new ViewResource(){
                    @Override
                    public URL getURL() {
                        return url;
                    }
                };
            }
            catch (IOException e) {
                throw new FacesException(e);
            }
        }

        return super.createViewResource(context, resourceName);
    }

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

}

(警告:这只是基本的启动示例!这将在每个请求上创建一个新的临时文件,您应该自己发明一种重用/缓存系统)

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

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

注意:上述内容都是针对JSF 2.2版本的。对于发现此答案的JSF 2.0 / 2.1用户,您应该使用ResourceResolver,本答案提供了一个示例:从外部文件系统或数据库获取Facelets模板/文件。重要提示:在JSF 2.2中,ResourceResolver已弃用,推荐使用ResourceHandler#createViewResource()。

我正在寻找一种方法,不必将临时文件存储在Web服务器上。是否有其他的替代方式可以直接使用内存数据?也许可以使用自定义JSF组件/标签等。 - user3027786
你可以创建一个自定义的URLStreamHandlerFactoryURLStreamHandler,就可以实现这个功能。我无法举出具体的例子,敬请谅解。 - BalusC
问题:在那里访问后备bean对象和/或将某些参数传递给资源处理程序是否可能? - user3027786
您可以在资源处理程序中使用CDI @Inject,或通过Application#evaluateExpressionGet()以编程方式评估EL。当然,通过相应地操作src(如src="/dynamic.xhtml?param=value")传递参数是可行的,“通常的方法”,您只需要使用例如String#split()resourceName上进行自己的解析。请注意,src中路径的格式完全由您选择。例如,src="dynamic:foo,bar,baz"等与if (resourceName.startsWith("dynamic:"))检查相结合。 - BalusC
我尝试了这个方法,结果很好。但是我无法在那里@Inject我的bean。我们需要特别注意什么才能注入一个bean吗? - user3027786
我使用URLStreamHandlerFactory和URLStreamHandler解决了这个问题。谢谢。 - user3027786

0

JSF 2.2和自定义URLStream处理程序的解决方案

public class DatabaseResourceHandlerWrapper extends ResourceHandlerWrapper {

private ResourceHandler wrapped;

@Inject
UserSessionBean userBeean;

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

@Override
public Resource createResource(String resourceName, String libraryName) {
    return super.createResource(resourceName, libraryName); //To change body of generated methods, choose Tools | Templates.
}

@Override
public ViewResource createViewResource(FacesContext context, String resourceName) {
    if (resourceName.startsWith("/dynamic.xhtml?")) {
        try {
            String query = resourceName.substring("/dynamic.xhtml?".length());
            Map<String, String> params = splitQuery(query);
            //do some query to get content
            String content = "<ui:composition"
                    + " xmlns='http://www.w3.org/1999/xhtml' xmlns:ui='http://java.sun.com/jsf/facelets'"
                    + " xmlns:h='http://java.sun.com/jsf/html'> MY CONTENT"
                    + "</ui:composition>";

            final URL url = new URL(null, "string://helloworld", new MyCustomHandler(content));
            return new ViewResource() {
                @Override
                public URL getURL() {
                    return url;
                }
            };
        } catch (IOException e) {
            throw new FacesException(e);
        }
    }

    return super.createViewResource(context, resourceName);
}

public static Map<String, String> splitQuery(String query) throws UnsupportedEncodingException {
    Map<String, String> params = new LinkedHashMap<>();
    String[] pairs = query.split("&");
    for (String pair : pairs) {
        int idx = pair.indexOf("=");
        params.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
    }
    return params;
}

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

static class MyCustomHandler extends URLStreamHandler {

    private String content;

    public MyCustomHandler(String content) {
        this.content = content;
    }

    @Override
    protected URLConnection openConnection(URL u) throws IOException {
        return new UserURLConnection(u, content);
    }

    private static class UserURLConnection extends URLConnection {

        private String content;

        public UserURLConnection(URL url, String content) {
            super(url);
            this.content = content;
        }

        @Override
        public void connect() throws IOException {
        }

        @Override
        public InputStream getInputStream() throws IOException {
            return new ByteArrayInputStream(content.getBytes("UTF-8"));
        }
    }

}

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接