4

What is the best way to enable my webapplication to use JSF files stored in the database? I'd like to be able to dynamically (during runtime) create new JSF pages which will be made available without having to redeploy the application.

So in other words: I would like to store the bigger part of my JSF pages in the database and would like JSF to use the database as a datasource for getting JSF files.

I've thought long about a solution and found some possible ways. However, I haven't been able to implement either of them.

  1. Whenever a new page has to be added/removed: manipulate the files in the classpath (e.g. remove or add a file to the .war file)
  2. Extending the classpath of the webapplication so it will be able to get files from an at runtime defined location (i.e. /tmp or directly using a database connection)
  3. Provide JSF with a way to find resources another way ( this doesn't seem possible? )

My environment:

  • Java SE 6
  • Jetty as servlet container
  • Mojarra as jsf implementation

Now, my question:

Is it possible for someone to let JSF find pages at a location other than the default classpath, preferably the database?

Any response is greatly appreciated!

sdegroot
  • 237
  • 3
  • 11
  • Depends heavily on the view technology. JSF is just a MVC framework. Are you using JSP or Facelets as view technology? If JSP, forget it. If Facelets, your proposed ways 1 and 2 should have worked flawlessly. However, using Jetty might have been influencial in the complete picture. – BalusC Jun 01 '11 at 13:49
  • Facelets it is then. However, I have no idea how I can achieve the first two possibilities, can you comment on that? I do however have made progress on the third possibility, [click](http://download.oracle.com/javaee/6/api/javax/faces/view/facelets/ResourceResolver.html). Is this the way to go? – sdegroot Jun 01 '11 at 14:36

2 Answers2

2

1: Whenever a new page has to be added/removed: manipulate the files in the classpath (e.g. remove or add a file to the .war file)

This is definitely possible if the WAR is expanded. I am not sure about Jetty, but it works for me with Mojarra 2.x on Tomcat 7 and Glassfish 3. Just writing the file to the expanded WAR folder the usual Java IO way suffices.

File file = new File(servletContext.getRealPath("/foo.xhtml"));

if (!file.exists()) {
    OutputStream output = new FileOutputStream(file);

    try {
        output.write(bytes); // Can be bytes from DB.
    } finally {
        output.close();
    }
}

This needs to be executed before the FacesServlet kicks in. A Filter is a perfect place. See also this related answer:


2: Extending the classpath of the webapplication so it will be able to get files from an at runtime defined location (i.e. /tmp or directly using a database connection)

You can package Facelets files in a JAR file and put it in the classpath and provide a Facelets ResourceResolver which serves the files from the JAR on when no match is found in WAR. You can find complete code examples in the following answers:


3: Provide JSF with a way to find resources another way ( this doesn't seem possible? )

You've plenty of play room in the custom ResourceResolver.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 1
    I've got it working with the ResourceResolver. Thank you for your clear answer! – sdegroot Jun 01 '11 at 15:07
  • @sdegroot, can you post your ResourceResolver? I'm trying to do the same thing as you, but I having a hard time writing the xhtml file I've got from the DB and returning the correct URL from the `resolveUrl` method. – Felipe Reis Nov 06 '13 at 18:27
  • 1
    @Felipe: you missed the complete code examples in the given two links? The text in blue are links. When you put your mouse on top until it turns into a hand, then click the left button. It will open the link in your browser. – BalusC Nov 06 '13 at 18:33
  • Thanks BalusC. I did look at those links, but since @sdegroot case is exactly the same as mine I was hoping that he would share his code. I managed to use the ResourceResolver, and from the resolveURL method I was getting the file from the database, but I was having a hard time writing the file in the temp folder and returning the correct URL. – Felipe Reis Nov 08 '13 at 16:32
  • Eventually I could solve my own problems. I've used File#createTempFile(), and then file#toURI#toURL. Thank you BalusC and sdgroot! – Felipe Reis Nov 08 '13 at 16:33
0

Nice question. BalusC's answer is - as always - complete and right.

However, if your point is to create an application where gui is built dynamically, there is a way that might serve you better (depending on what you really want to achieve).

JSF views are similar to Swing forms - they are just a bunch of JavaBeans(tm) glued together. The big difference is that when a field is bound to an EL expression, you do not use standard accessors, but rather a special method (setValueExpression).

This means you can build your GUI from objects (the concrete classes can be found in javax.faces.component.html) in a pure programmatic way and then use binding attribute to show it on page. Something like:

<h:form>
    <h:panelGrid binding="#{formBuilder.component}"/>
</h:form>

And then in the managed formBuilder bean:

@PostConstruct
public void init() {
    HtmlInputText hit = new HtmlInputText();
    // properties are easy:
    hol.setStyle("border: 2px solid red");
    // binding is a bit harder:
    hit.setValueExpression("value", expression("#{test.counter}", String.class));

    HtmlOutcomeTargetLink hol = new HtmlOutcomeTargetLink();
    hol.setValue("link leading to another view");
    hol.setOutcome("whatever");

    component = new UIPanel();
    component.getChildren().add(hit);
    component.getChildren().add(hol);
}

private ValueExpression expression(String s, Class c){
    return FacesContext.getCurrentInstance().getApplication().getExpressionFactory().createValueExpression(
            FacesContext.getCurrentInstance().getELContext(),
            s, c
    );
}

The example above builds a static panel, but it would be possible to:

  • create an object model of your GUI
  • map the model to database (with hibernate or another orm)
  • write some kind of adapter or bridge to build JSF objects from your object model
  • make a managed bean that receives the form id, grabs the relevant form from database, builds a JSF panel out of it and presents it as a property, ready to be bound.

This way you could have just one static xhtml with a single tag and use it to present any number of dynamic forms.

As I said, this method could be better than just storing files, but not necessarily. If you just want to save yourself the hassle of redeployment, this is a huge overkill (then again, you do NOT need to redeploy JSF applications just to change forms). If on the other hand your goal is to have something like user-defined and edited forms, having a good object model and storing it in a proper way could be a good idea.

The bumps ahead would be:

  • navigation (perhaps a custom navigation handler would suffice?)
  • problems with generating plain html
  • possibly some problems with lifecycle of view scoped forms
fdreger
  • 12,264
  • 1
  • 36
  • 42
  • Thanks for the complementary answer! Maybe I can do something with this. To clarify: I'd like to be able to add new pages/templates to the webapplication during runtime. Think of it as a CMS: someone would be able to write their own HTML (in this case JSF/XHTML). What would be your recommendation? – sdegroot Jun 03 '11 at 08:45
  • I do not really recommend anything. If your users are supposed to know facelets and JSF, then BalusC's solution will suffice. But then, you do not need it, because it is ridiculously easy for a programmer to add xhtml files to a running JSF app (served wars are usually unzipped, so no problem with adding files to context; xhml is not compiled, so you can just save additional files with no restarting or other fuss). If your users are just normal users and you need some kind of WYSIWYG form builder - my solution will probably work a bit better. But who knows, there is too many variables. – fdreger Jun 03 '11 at 21:16
  • In your example, you still need at least one facelet that does nothing but provides a URL to be resolved and a binding to some GUIBuilder bean. If I want to have completely dynamic URLs with no facelet glue, is there any way to bypass the need for a facelet and just get invoked directly from the faces lifecycle somehow? – Mark Murfin May 31 '16 at 12:12
  • @MarkMurfin These are separate things: do you need a "completely dynamic url" or "no facelet glue"? – fdreger May 31 '16 at 20:23
  • I think I found what I was looking for here: http://jdevelopment.nl/authoring-jsf-pages-pure-java/. I really want to author faces resources without using facelets at all. – Mark Murfin May 31 '16 at 20:41
  • @MarkMurfin sure, whatever you wish (although you will hit problems once you start dealing with the life cycle of componentes). Jus note that the "facelet glue" is a single file, that can be stashed inside a library jar and reused for any number of different urls and views. It does not need to be visible to the person authoring the pages. – fdreger Jun 01 '16 at 08:01