質問

I'm working on a JSF 2 application that is composed of a core component that can be extended by (usually client specific) code. Generally speaking: the extended parts of the application precede the core ones.

For the Java code this is done by using conventional mechanisms. For the presentation layer, we use a javax.faces.view.facelets.ResourceResolver implementation that first tries to find resources in the extension jar before the core resources are used.

We use a lot of composite components for reusable markup. Think of components for showing addresses, salaries, etc.

The Facelets are causing major headaches when it comes to the extendable nature of the application and I'm starting to wonder if there is a solution for the problems that we run into.

What we want to achieve, is having a standard interface for composite components, but override the implementation somehow by resolving multiple implementations, out of which the extended implementation should precede the core implementation.

The idea of course is, that the core defines the standard layout/templating of the application, and extensions define client-specific formatting options, or hiding/showing part of the managed model that the application does.

For example:

<ui:composition 
    xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:cc="http://java.sun.com/jsf/composite"
    xmlns:ui="http://java.sun.com/jsf/facelets" 
    xmlns:pui="http://java.sun.com/jsf/composite/pui">

    <cc:interface>
        <cc:attribute name="saveButtonLabel" />
        <cc:attribute name="saveButtonIcon" />
        <cc:attribute name="saveButtonIconPosition" />
    </cc:interface>

    <cc:implementation>
        <pui:pension_plan_custom_state pensionPlanBean="#{pensionPlanBean}" />
        <pui:pension_plan_general pensionPlanBean="#{pensionPlanBean}" />
        <pui:pension_plan_pension_plan pensionPlanBean="#{pensionPlanBean}" />
        <pui:pension_plan_salary pensionPlanBean="#{pensionPlanBean}" />
        <pui:pension_plan_investments pensionPlanBean="#{pensionPlanBean}" />
        <pui:pension_plan_benefit_types benefitTypes="#{pensionPlanBean.benefitTypesViewData}" />

        <div class="buttons">
            <!-- Irrelevant -->
        </div>
    </cc:implementation>
</ui:composition>

Take the pension_plan_benefit_types composite component for example. Its interface prescribes that an attribute by the name benefitTypes is to be given by the client. If a client wants just this part of the screen to be overwritten by different content than the standard implementation would so, we need a place to overwrite this somewhere.

Also note, that the core does not know about the namespaces that the (optional) extension provides. The core doesn't really care, as long as the interface of the composite is stable and doesn't require changes.

As a last resort, the following has been tried (pseudo code):

<ui:composition
    xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:cc="http://java.sun.com/jsf/composite"
    xmlns:ui="http://java.sun.com/jsf/facelets" 
    xmlns:pui="http://java.sun.com/jsf/composite/pui">

    <cc:interface>
        <cc:attribute name="benefitTypes" required="true" type="com.foo.bar.PensionPlanBenefitTypesViewData" />
    </cc:interface>

    <cc:implementation>
        <ui:include src="/resources/pui/markup/pension_plan_benefit_types_markup.xhtml" />
    </cc:implementation>
</ui:composition>

Where the idea is, that the javax.faces.view.facelets.ResourceResolver will come to our rescue.

It 'kind of works', but depending on the implementation we have to write for the composite components, we run into all kinds of issues in the infamous build,- vs rendertime department of the JSF lifecycle. It basically does not work the way we would expect it to.

The big question now is:

Is there a way in which we have both a stable contract/namespace and have multiple implementations that are dynamically resolved?

Hope someone can shed some light on this, thanks for your input.

Kind regards,

Rens

役に立ちましたか?

解決 2

We have extended the ResourceHandlerWrapper in such a way that the classpath will be scanned (first) when xhtml resources from WEB-INF/resources are requested to be loaded.

By defining an explicit classpath in a MANIFEST.MF file, we can let the extension jar files take precedence over the standard implementation. By doing so, we can programmatically generate additional content too, that can be shown in the UI to the developer (when running in development mode) to let him/her know that an 'overriding' implementation could be written in the extension project.

他のヒント

This kind of situations has been addressed in JSF 2.2 with the resource library contract feature. With that feature, it is possible to have multiple implementations of the same composite component according to its localization and active contracts. Note that the localization / contracts are active per view.

But the option that maybe suits you better is use:

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

or

<ui:decorate template="#{...}">

And provide the template name from a managed bean or from the composite component class itself (cc:interface componentType=...). I would recomend use the last version of MyFaces Core in this case, because its algorithm has been optimized specifically for these cases (small view state size and fast performance). I don't think you need to deal with ResourceResolver logic here.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top