Включить динамический контент, содержащий теги / компоненты 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"));
}
}
}
}