How to create a modular JSF 2.0 application?

13,970

Solution 1

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:

Solution 2

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.

Solution 3

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.
Share:
13,970

Related videos on Youtube

SplinterReality
Author by

SplinterReality

An IT pro from California who learned Japanese was harder than C, and never went home.

Updated on September 01, 2020

Comments

  • SplinterReality
    SplinterReality almost 4 years

    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)

    • maple_shaft
      maple_shaft about 13 years
      What a good question! One that I am interested in myself. I hope that somebody has a good answer to share.
    • SplinterReality
      SplinterReality about 13 years
      @maple_shaft Did this answer your question, or do I need to open a community wiki about building modular JSF 2.0 apps? It's pretty straight-forward to me at this point, but that doesn't mean it is to everyone reading this.
    • maple_shaft
      maple_shaft about 13 years
      Yes this makes perfect sense to me. At a later time after my current project goes out the door I plan on refactoring out common modules for reuse in future applications and was wanting to know how to do this. I favorited this question so I can refer back to it now. Thank you for asking it!
  • SplinterReality
    SplinterReality about 13 years
    This is exactly what I was after. Knew there had to be a simple way to do this, I just didn't know how. Thank you very much!
  • SplinterReality
    SplinterReality about 13 years
    Tested, and this does indeed create the solution I was wanting. My final solution will likely be quite nuanced compared with this, but I put the resolver in the .war file, and will simply specify that views go in folder X in the module .jar file as part of the interface contract. This keeps the programming concerns of the modules and application separated better, and allows me to resolve specific resources based on the module they originate in (Only the application knows this with certainty) to avoid namespace collision concerns.
  • SplinterReality
    SplinterReality over 12 years
    That would have been a nice article to see. I'm sure it will add some to people's knowledge who come looking for this topic here. Thanks for posting this link!
  • martinyyyy
    martinyyyy almost 9 years
    "Fun fact"... this is still needed with Websphere 8.0.0.8 even though it supports servlet 3.0!
  • Rokko_11
    Rokko_11 over 8 years
    Great solution, but what happens if more than one module finds a resource? And how can I accomplish if I would like the modules not to include their xhtml to ui:include but to attach it?
  • BalusC
    BalusC over 8 years
    @Rokko: ordering can be defined in web-fragment.xml. No idea what you mean with include vs attach.
  • Rokko_11
    Rokko_11 over 8 years
    I mean by attaching following: The main template defines something like "<div id="content"><ui:include value="before-content"></div> and modules can add their content at this location. So if module 1 adds his content "<div>content 1</div>" and module 2 adds "<div>content 2</div>" then it would result in the output "<div id="content"><div>content 1</div><div>content 2</div></div>". Or is this a totally different case?
  • Rapster
    Rapster over 5 years
    @BalusC I'd like to see how Servlet 3.0 implements that resolver, could you please provide a link to source code?
  • BalusC
    BalusC over 5 years
    @Rapster: Depends on the container. Just put a breakpoint in ServletContext#getResource() and advance.
  • Rapster
    Rapster about 5 years
    @BalusC Since ResourceResolver is deprecated, it'd be nice to update this post with ResourceHandler (see arjan-tijms.omnifaces.org/p/jsf-22.html#809)
  • BalusC
    BalusC about 5 years
    @Rapster: as per the last paragraph in the answer, since Servlet 3.0 the entire ResourceResolver approach is not necessary. JSF 2.2 is not compatible with Servlet 2.5 anyway. You can just drop the ResourceResolver. All you have to do is just placing resources in /META-INF/resources of the JAR. See also the "Packaging Facelets files" link in bottom of answer for an example folder structure. In case you really just wanted to see a concrete example of a ResourceHandler from my hand, see stackoverflow.com/q/13292272