2

I am new to JSF and am struggling with passing data to my backing bean. I have a bean that should read and save data to one specific table. This table has two columns "Property" and "Val". The simplified version of this class looks like this:

@ManagedBean
@SessionScoped
 public class GlobalProperties implements Serializable {

    // private void setKey(param);
    // private String getKey();      
    // some more attributes and getters / setters
    private String property; // + getter/setter    

    private void saveProperty() throws SQLException {
        try {
            dbHandler = dbConnection.connect();
            ps = dbHandler.prepareStatement("UPDATE TABLEXXX SET VAL = '" + getValue() + "' WHERE PROPERTYKEY = '" + getProperty() + "'");
            ps.executeQuery();
            } catch (SQLException ex) {
            Logger.getLogger(GlobalProperties.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
            if (dbHandler != null) {
                dbHandler.close();
            }
        }        
    }

    private void readProperty() throws SQLException {
        try {
            dbHandler = dbConnection.connect();
            ps = dbHandler.prepareStatement("SELECT VAL FROM TABLEXXX WHERE PROPERTYKEY = '" + getProperty() + "'");
            rs = ps.executeQuery();
            while (rs.next()) {
                setValue("VAL");
            }
        } catch (SQLException ex) {
            Logger.getLogger(GlobalProperties.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            if (dbHandler != null) {
                dbHandler.close();
            }
        }    
    }        
}

This class is being used on several pages. On each page it is being used, it needs to be filled with a different value for the property-attribute since I have to read different keys from the table. On JSF-side there is a text input which displays the value and sets it when user clicks a save button.

Whenever an instance of this class is created, I need to pass an information to the class about what key it has to read. But i cannot find an appropriate mechanism on JSF-side to accomplish that, since it is heavily important, that the user can not change that value.

My last try was to use the "preRenderView"-Event like this:

  <f:metadata>
        <f:event type="preRenderView" listener="#{globalProperties.setKey('SYSTEM_NAME')}" />
    </f:metadata>

Is that the right way?

What is the best practice to set a property in a ManagedBean in the background, without the need of any user action and in a safe way, so that it can not be manipulated by the user?

Thank you guys.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Caz
  • 25
  • 6

1 Answers1

3

<f:event type="preRenderView"> is invoked on every request. That may be OK for request scoped beans, but is unnecessary for view/session scoped beans. See also What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?

On the other hand, whilst the preRenderView approach will work, the JSF view should be declarative. The model (the backing bean in this specific perspective) should actually be performing the initialization based on the view and not the other way round. So your intuition was right when you got doubts on this.

JSF doesn't have a dedicated approach for this. The closest declarative approach is using <f:attribute> in <f:metadata> ("define a metadata attribute on the view").

<f:metadata>
    <f:attribute name="key" value="SYSTEM_NAME" />
</f:metadata>

This is available by UIViewRoot#getAttributes() in a.o @PostConstruct:

String key = (String) context.getViewRoot().getAttributes().get("key");

Or when you happen to use OmniFaces:

String key = Faces.getMetadataAttribute("key");

The OmniFaces showcase application also uses this approach to declare paths to documentation and source code (see for example /push/socket.xhtml source of <o:socket> showcase).

The alternative approach is to have an application wide mapping of keys by view ID and rely on the view ID instead.

Map<String, String> keysByViewId = new HashMap<>();
keysByViewId.put("/foo.xhtml", "SYSTEM_NAME");

String key = keysByViewId.get(context.getViewRoot().getViewId());
Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555