Включить динамический контент, содержащий теги / компоненты JSF из потока

Я работаю над приложением, в которое я хотел бы включить динамический контент XHTML из потока. Чтобы справиться с этим, я написал расширение taghandler, которое выгружает динамический контент XHTML в компонент вывода как

UIOutput htmlChild = (UIOutput) ctx.getFacesContext().getApplication().createComponent(UIOutput.COMPONENT_TYPE);
htmlChild.setValue(new String(outputStream.toByteArray(), "utf-8"));

Это прекрасно работает для содержимого XHTML, у которого нет тегов JSF. Если в моем динамическом контенте XHTML есть теги JSF, например <h:inputText value="#{bean.item}"/>затем они печатаются как обычный текст. Я хочу, чтобы они отображались как поля ввода. Как мне этого добиться?

2 ответа

Решение

По сути, вы должны использовать <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 из внешней файловой системы или базы данных. Важная заметка: ResourceResolver устарела в JSF 2.2 в пользу ResourceHandler#createViewResource(),

Мое решение для JSF 2.2 и пользовательский обработчик URLStream

открытый класс 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"));
        }
    }

}

}

Другие вопросы по тегам