如何创建模块化 JSF 2.0 应用程序?

发布于 2024-11-13 01:13:37 字数 853 浏览 2 评论 0原文

我有一个具有明确定义的界面的应用程序。它使用 CDI 来解析模块(具体来说,它使用 API 接口上的 Instance<> 注入点来解析模块),并通过接口毫无问题地来回传递各种数据。我故意将 API 和实现分开,模块仅从 API 继承以避免紧密耦合,应用程序仅通过运行时依赖关系了解模块,并通过 API 完成数据传递。应用程序无需模块即可正常运行,只需将 jar 放入 WEB-INF/lib 文件夹并重新启动应用程序服务器即可添加模块。

我遇到的问题是我希望模块创建视图的一部分,因此我想以可移植的方式调用 JSF 组件,或者从模块中包含以获得它呈现其视图。我已经解决了我想要调用的模块,并且已经准备好对该模块接口的引用。我最初想到的方法是执行 ui:include ,要求模块提供其视图模板的位置,但我不知道如何以有意义的方式回答该查询,因为视图解析是从应用程序完成的根,而不是库根。

执行摘要是,我不知道如何使用 JSF for .xhtml(模板/组件)文件从应用程序跳转到库。

使用 CC 会很好,但如何指定在运行时需要特定的 CC 实例,而不是将其硬编码到页面中?

我当然可以直接调用应用程序代码并要求它提供标记,但这看起来确实很暴力,而且一旦我有了标记,我不确定如何告诉 JSF 对其进行评估。也就是说,我可以想象一个组件将采用资源路径,获取标记并对其进行评估,返回完整的标记,我只是不知道如何实现它。

如果可能的话,我宁愿避免强迫模块开发人员采用重型 UIComponent 方法,这意味着要么采用动态方式执行 ui:include (或某种等效方式),要么采用动态方式调用 CC。 (我不介意在应用程序中编写一次 UIComponent 方法,如果这能让模块开发人员的生活更轻松)

关于我应该在哪里解决这个问题有什么建议吗? (如果我先找到答案,我会在这里发布答案)

I have an application with a well defined interface. It uses CDI for resolution of the modules, (Specifically it uses Instance<> injection points on API interfaces to resolve modules) and passes various data back and fourth via the interfaces without issue. I've intentionally kept the API and implementation separate, and the modules only inherit from the API to avoid tight coupling, and the application only knows of the modules through runtime dependancies, and data passing accomplished via the APIs. The application runs fine without the modules, which can be added simply by dropping the jar into the WEB-INF/lib folder and restarting the app server.

Where I'm running into issues is that I want the modules to create a portion of the view, and I therefor want to invoke, in a portable way, either a JSF component, or do an include from the module in order to have it render its view. I already have resolved what module I want to invoke, and have references to the module's interface ready. The way I initially thought to do this was to do a ui:include that asks the module to supply where it's view template is, but I have no idea how to answer that query in a meaningful way, as view resolution is done from the application root, not the library root.

The executive summary is that I have no idea how to jump the gap from Application to Library using JSF for .xhtml (template/component) files.

Using a CC would be nice, but how do I specify that I want a particular CC instance at runtime, instead of having that hard coded into the page?

I can of course invoke the application code directly and ask it for markup, but this seems really brute force, and once I have the markup, I'm not sure exactly how to tell JSF to evaluate it. That said, I can imagine a component that would take the resource path, grab the markup and evaluate it, returning the completed markup, I just don't know how to implement that.

I'd rather avoid forcing module developers to go the heavy duty UIComponent approach if possible, which means either a dynamic way of doing ui:include (or some equivalent) or a dynamic way of invoking CCs. (I don't mind coding the UIComponent approach ONCE in the application if that's what it takes to make module developers' lives easier)

Any suggestions on where I should look to figure this out? (I'll post the answer here if I find it first)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

星光不落少年眉 2024-11-20 01:13:37

我知道您的问题基本上可以归结为如何在 JAR 中包含 Facelets 视图?

您可以通过放置自定义 ResourceResolver 在 JAR 中。

public class FaceletsResourceResolver extends ResourceResolver {

    private ResourceResolver parent;
    private String basePath;

    public FaceletsResourceResolver(ResourceResolver parent) {
        this.parent = parent;
        this.basePath = "/META-INF/resources"; // TODO: Make configureable?
    }

    @Override
    public URL resolveUrl(String path) {
        URL url = parent.resolveUrl(path); // Resolves from WAR.

        if (url == null) {
            url = getClass().getResource(basePath + path); // Resolves from JAR.
        }

        return url;
    }

}

在 web 应用程序的 web.xml 中进行如下配置:

<context-param>
    <param-name>javax.faces.FACELETS_RESOURCE_RESOLVER</param-name>
    <param-value>com.example.FaceletsResourceResolver</param-value>
</context-param>

假设您在 random.jar< 中有一个 /META-INF/resources/foo/bar.xhtml /code>,那么你可以用通常的方式

<ui:include src="/foo/bar.xhtml" />

甚至动态地

<ui:include src="#{bean.path}" />

包含它。注意:从 Servlet 3.0 和更新的 JBoss/JSF 2.0 版本开始,如果你保留文件位于 /META-INF/resources 文件夹中。上面的 ResourceResolver 仅在 Servlet 2.5 或更早的 JBoss/JSF 版本中是必需的,因为它们在 META-INF 资源解析中存在错误。

另请参阅:

I understand that your question basically boils down to How can I include Facelets views in a JAR?

You can do this by placing a custom ResourceResolver in the JAR.

public class FaceletsResourceResolver extends ResourceResolver {

    private ResourceResolver parent;
    private String basePath;

    public FaceletsResourceResolver(ResourceResolver parent) {
        this.parent = parent;
        this.basePath = "/META-INF/resources"; // TODO: Make configureable?
    }

    @Override
    public URL resolveUrl(String path) {
        URL url = parent.resolveUrl(path); // Resolves from WAR.

        if (url == null) {
            url = getClass().getResource(basePath + path); // Resolves from JAR.
        }

        return url;
    }

}

Configure this in webapp's web.xml as follows:

<context-param>
    <param-name>javax.faces.FACELETS_RESOURCE_RESOLVER</param-name>
    <param-value>com.example.FaceletsResourceResolver</param-value>
</context-param>

Imagine that you've a /META-INF/resources/foo/bar.xhtml in random.jar, then you can just include it the usual way

<ui:include src="/foo/bar.xhtml" />

or even dynamically

<ui:include src="#{bean.path}" />

Note: since Servlet 3.0 and newer JBoss/JSF 2.0 versions, the whole ResourceResolver approach is not necessary if you keep the files in /META-INF/resources folder. The above ResourceResolver is only mandatory in Servlet 2.5 or older JBoss/JSF versions because they've bugs in META-INF resource resolving.

See also:

近箐 2024-11-20 01:13:37

我正在寻找有关同一主题的信息并发现了此链接: 操作方法:使用 CDI 和 PrettyFaces 的模块化 Java EE 应用程序 这对我来说非常有效。

I was looking for information on the same topic and came across this link: How-to: Modular Java EE Applications with CDI and PrettyFaces which worked really well for me.

在巴黎塔顶看东京樱花 2024-11-20 01:13:37

顺便说一句..当您使用Seam Solder(目前已集成到apache deltaspike中)时,您可以避免实现自己的资源解析器,这是一个非常有用的库,补充了CDI(典型的Java EE 6组件模型)

我也尝试过模块化在 jsf 应用程序中。基本上,我构建了一个带有工具栏的模板界面,其中充满了每个模块提供的按钮。通常,您将通过提供字符串列表作为命名对象来完成此操作:

@Produces
@SomethingScoped
@Named("topMenuItems")
public List<String> getTopMenuItems(){
return Arrays.asList("/button1.xhtml", "/button2.xhtml", "/button3.xhtml");
}

请注意每个按钮如何来自 jsf 应用程序的不同模块。
模板界面包含一个面板,

您可以通过以下方式在标记中使用它(风险自负;)):

....
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
....
<xy:toolbar>
  <xy:toolbarGroup>
    <c:forEach items="#{topMenuItems}" var="link">
      <ui:include src="#{link}" />
    </c:forEach>
  </xy:toolbarGroup>
</xy:toolbar>
<xy:panel>
  <ui:include src="#{contentPath}"/>
</xy:panel>

这是工具栏和内容面板。

一个简单的按钮或视图定义可能如下所示:

<ui:composition ...>
    <xy:commandButton actionListener="#{topMenuController.switchContent()}"
        value="Test" id="testbutton" />
</ui:composition>

让我们将此工件命名为 view1.xhtml

当按下此按钮时(不会使用 actionListener 触发回发,我们希望使用 ajax 重新加载内容),控制器中的 switchContentMethod 可能会更改 getContentPath 返回的字符串:

public void switchContent(){
    contentPath = "/view1.xhtml";
}

@Produces
@SomethingScoped
@Named("contentPath")
public String getContentPath(){
    return contentPath;
}

现在您可以使用菜单栏中的按钮更改面板中显示的视图,该按钮无需重新加载页面即可进行导航。

一些建议(或“我学到的东西”):

  1. 您可能想为 getTopMenuItems 方法选择较大的范围
  2. 不要嵌套 ui:include 标记。不幸的是,这是不可能的(例如,您的 view1.xhtml 不能包含其他组合)。我真的
    希望这样的事情成为可能,因为你真的可以建造
    带有此功能的模块化 jsf 视图,有点像 portlet,只是没有
    portlet.. =D
  3. 在 tabviews 等容器组件中执行 ui:include 也被证明是有问题的。
  4. 一般来说,不建议混合使用 JSTL (c:forEach) 和 JSF。尽管如此,我发现这是 ui:repeat 工作的唯一方法
    评估太晚,例如您包含的内容不会出现。

By the way.. you can avoid implementing your own resource resolver when you're using seam solder (currently being integrated into apache deltaspike) which is a really useful library complementing CDI (your typical Java EE 6 component model)

I too experimented with modularity in jsf applications. Basically I built a template interface with a toolbar which gets filled with buttons provided by each module. Typically you will do this by providing a List of Strings as a Named Object:

@Produces
@SomethingScoped
@Named("topMenuItems")
public List<String> getTopMenuItems(){
return Arrays.asList("/button1.xhtml", "/button2.xhtml", "/button3.xhtml");
}

Notice how each of the buttons may come from a different module of the jsf application.
The template interface contains a panel where

you can use it in your markup the following way (at your own risk ;) ) :

....
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
....
<xy:toolbar>
  <xy:toolbarGroup>
    <c:forEach items="#{topMenuItems}" var="link">
      <ui:include src="#{link}" />
    </c:forEach>
  </xy:toolbarGroup>
</xy:toolbar>
<xy:panel>
  <ui:include src="#{contentPath}"/>
</xy:panel>

This was the toolbar and the content panel.

a simple button or view definition may look like this:

<ui:composition ...>
    <xy:commandButton actionListener="#{topMenuController.switchContent()}"
        value="Test" id="testbutton" />
</ui:composition>

lets name this artifact view1.xhtml

When this button is pushed (which doesn't trigger a postback using the actionListener, we want to reload the content using ajax) the switchContentMethod in your controller may change the string returned by getContentPath :

public void switchContent(){
    contentPath = "/view1.xhtml";
}

@Produces
@SomethingScoped
@Named("contentPath")
public String getContentPath(){
    return contentPath;
}

now you can change the view displayed in the panel using the button in the menubar which sort of gives you navigation without page reloads.

Some advice ( or 'what I've learned' ) :

  1. You may wanna choose a large scope for the getTopMenuItems method
  2. Don't nest the ui:include tag. Unfortunately this is not possible (e.g. your view1.xhtml cannot include another composition). I really
    would like something like this to be possible as you can build really
    modular jsf views with this, kinda like portlets only without the
    portlets.. =D
  3. doing ui:include in container components like tabviews also proves problematic.
  4. generally it's not advisable to mix JSTL (c:forEach) and JSF. Still I found this to be the only way working as ui:repeat gets
    evaluated too late e.g. your included content does not appear.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文