简体   繁体   English

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

[英]How to create a modular JSF 2.0 application?

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. 它使用CDI来解析模块,(特别是它使用API​​接口上的Instance <>注入点来解析模块)并通过接口传回各种数据而没有问题。 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. 我故意将API和实现分开,并且模块仅从API继承以避免紧密耦合,并且应用程序仅通过运行时依赖性知道模块,并且通过API完成数据传递。 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. 应用程序在没有模块的情况下运行正常,只需将jar放入WEB-INF / lib文件夹并重新启动应用服务器即可添加。

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. 我遇到问题的地方是我希望模块创建视图的一部分,因此我想以可移植的方式调用JSF组件,或者从模块中执行包含以获取它渲染它的观点。 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. 我最初认为这样做的方法是做一个ui:include要求模块提供它的视图模板的位置,但我不知道如何以有意义的方式回答该查询,因为视图解析是从应用程序完成的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. 执行摘要是我不知道如何使用JSF为.xhtml(模板/组件)文件从应用程序跳到库。

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? 使用CC会很好,但是如何在运行时指定我想要一个特定的CC实例,而不是将其硬编码到页面中?

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. 我当然可以直接调用应用程序代码并要求它进行标记,但这似乎是非常强大的,一旦我有了标记,我就不确定如何告诉JSF来评估它。 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. 如果可能的话,我宁愿避免强迫模块开发人员采用重型UIComponent方法,这意味着要么采用动态方式执行ui:include(或某些等效方法),要么采用动态方式调用CC。 (I don't mind coding the UIComponent approach ONCE in the application if that's what it takes to make module developers' lives easier) (我不介意在应用程序中编写UIComponent方法ONCE,如果这是使模块开发人员的生活更轻松的话)

Any suggestions on where I should look to figure this out? 关于我应该在哪里找出这个问题的任何建议? (I'll post the answer here if I find it first) (如果我先找到答案,我会在这里发布答案)

I understand that your question basically boils down to How can I include Facelets views in a JAR? 我知道您的问题基本上归结为如何在JAR中包含Facelets视图?

You can do this by placing a custom ResourceResolver in the JAR. 您可以通过在JAR中放置自定义ResourceResolver来完成此操作。

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: 在webapp的web.xml配置如下:

<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 想象一下,你在random.jar有一个/META-INF/resources/foo/bar.xhtml ,然后你就可以通常的方式包含它

<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. 注意:从Servlet 3.0和更新的JBoss / JSF 2.0版本开始,如果将文件保存在/META-INF/resources文件夹中,则不需要整个ResourceResolver方法。 The above ResourceResolver is only mandatory in Servlet 2.5 or older JBoss/JSF versions because they've bugs in META-INF resource resolving. 上述ResourceResolver仅在Servlet 2.5或更早的JBoss / JSF版本中是必需的,因为它们在META-INF资源解析中存在缺陷。

See also: 也可以看看:

我正在寻找有关同一主题的信息并且遇到了这个链接: 操作方法:带有CDI和PrettyFaces的模块化Java EE应用程序,对我来说非常有用。

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) 顺便说一下..当你使用缝焊(目前正在集成到apache deltaspike中)时,你可以避免实现自己的资源解析器,这是一个非常有用的库补充CDI(典型的Java EE 6组件模型)

I too experimented with modularity in jsf applications. 我也在jsf应用程序中尝试了模块化。 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. 注意每个按钮可能来自jsf应用程序的不同模块。 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 让我们为这个工件命名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 : 当按下此按钮(不使用actionListener触发回发时,我们希望使用ajax重新加载内容)控制器中的switchContentMethod可能会更改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 您可能希望为getTopMenuItems方法选择一个大范围
  2. Don't nest the ui:include tag. 不要嵌套ui:include标记。 Unfortunately this is not possible (eg your view1.xhtml cannot include another composition). 不幸的是,这是不可能的(例如你的view1.xhtml不能包含另一个组合)。 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 我真的希望这样的东西是可能的,因为你可以用这个构建真正的模块化jsf视图,有点像没有portlet的portlet .. = D
  3. doing ui:include in container components like tabviews also proves problematic. 做ui:包含像tabviews这样的容器组件也证明是有问题的。
  4. generally it's not advisable to mix JSTL (c:forEach) and JSF. 通常不建议混合使用JSTL(c:forEach)和JSF。 Still I found this to be the only way working as ui:repeat gets evaluated too late eg your included content does not appear. 我仍然发现这是唯一的ui工作方式:重复评估得太晚,例如你的内容没有出现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM