Enriching EL evaluation in JSF applications – retrieve values from property files or system properties

This article illustrates the use of a custom ELVariableResolver in JavaServer Faces applications. In this example, we extend the basic functionality of JSF EL resolution with the ability to evaluate any EL expresion – whether used for a managed (bean) property or somewhere in a JSF page – against properties in designated property files and the system properties defined for the JVM.

Note: it is easy to extend JSF using the Spring Framework with similar functionality – as I describe at length in this article: https://technology.amis.nl/blog/12306/leveraging-spring-beans-in-adf-faces-applications-and-using-the-spring-propertyplaceholderconfigurer-bean-to-dynamically-configure-bean-properties-from-external-files.

This article shows how the extensibility of the EL resolver mechanism in JSF can be leveraged and can serve as an example for other extensions – for example the ability to resolve EL expressions against MBeans (JMX), JNDI entries, a database table, and whatever else comes to mind.

The examples are shown using JDeveloper and ADF Faces – but are applicable to JSF in general.

Class PropertiesELResolver

The start of the JSF extension mechanism for us is the class javax.el.ELResolver that we need to extend. My class PropertiesELResolver extends ELResolver. Since I am only interested in evaluating expressions for returning values (and not for accepting new values to be set), I really only need to implement a single method: getValue(ELContext elContext, Object base, Object property).

This method is called to find out whether it is able to resolve an expression that consists of a base and a property. The base is the result of the evaluation of a part of the entire EL expression – starting from the left – and the property is the next ‘segment’ in that expression. For example: for the EL expression #{departmentBean.manager.spouse.lastName}, the based could be the result of evaluating departmentBean.manager and the property would then be spouse. The base can be empty: when the first segment is evaluated; in this example, the property would then be departmentBean. The method sets a flag on the ELContext (setPropertyResolved(boolean)) to indicate whether or not it managed to interpret the expression. It returns the result of the evaluation.

public class PropertiesELResolver extends ELResolver {

    private static Properties properties;

    @Override
    public Object getValue(ELContext elContext, Object base, Object property) {
        if (properties == null) {
            try {
                loadProperties();
            } catch (MalformedURLException e) {
                System.out.println(e.getMessage());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        String value = properties.getProperty((String)property);
        if (value == null) {
            // try with the system properties
            value = this.resolveSystemProperty((String)property);
        }
        elContext.setPropertyResolved(value != null);
        return value;
    }

The method relies on the class member properties – a java.util.Properties object – to find property values from. When it cannot find the value of a property in this object, it will attempt to find the property through the call to method resolveSystemProperty. This method inspects system and environment variables (note: it was copied from the PropertyPlaceholderConfigurer class in the Spring Framework). The getValue() method currently does not use the value for base at all. An implementation that would make life a little harder for developers but that would increase clarity and performance as well could make use of a dummy base object by only acting on expressions like #{dummy.value} (where dummy could be any string that is not used for a real managed bean, for example properties or conf).

This very un-robust implementation with far too many hard coded dependencies is only meant to demonstrate how the mechanics work – it is not an example of production ready code!

Image

The loadProperties() method is implemented to load various properties file from the file system and add all loaded values into a single Properties object. The file names of the property files are hard coded – except for one: the class will load either dev-env.properties or test-env.properties. Which of the two it will be, depends on another property:

Image

the property targetEnvironment that is set as JVM start up parameter.

A fourth properties file is not part of the web application at all – it is a file on the file system, referred to with an absolute reference:

Image

    private void loadProperties() throws MalformedURLException, IOException {
        InputStream inputStream;
        URL url;
        Properties properties = new Properties();

        // url = PropertiesELResolver.class.getClassLoader().getResource("WEB-INF/someWebResource.properties");
        // Get the inputStream
        inputStream = this.getClass().getClassLoader().getResourceAsStream("META-INF/project.properties");
        properties.load(inputStream);

        url = new URL("file:/c:/temp/global.properties");
        inputStream = url.openStream();
        properties.load(inputStream);

        //url = PropertiesELResolver.class.getClassLoader().getResource("WEB-INF/property_file_name.properties");
        String targetEnvironment = resolveSystemProperty("targetEnvironment");
        System.out.println("system prop targetEnvironment"+targetEnvironment);
        if (targetEnvironment== null) {
            targetEnvironment="dev";
        }
        inputStream = this.getClass().getClassLoader().getResourceAsStream("META-INF/"+targetEnvironment+"-env.properties");
        properties.load(inputStream);
        this.properties = properties;
    }

Configuring the ELVariableResolver in Faces-Config.xml

The class that extends ELResolver should be configured in the faces-config.xml file in order for it to be invoked during actual resolution of EL expressions. This is done with a simple element in the application element in faces-config.xml:

  <application>
    ...
    <el-resolver >nl.amis.jsf.PropertiesELResolver</el-resolver>
  </application>

Defining managed property values – relying on the values from Properties files

Managed properties can be defined on managed beans to pass instantiation values to managed beans right after they have been constructed. In this particular case, those values are defined through EL Expressions that cannot be resolved in the conventional manner. However, the newly configured PropertiesELResolver will be able to evaluate these expressions, as they refer to properties in various property files. The definition of the managed someBean is shown below. There is nothing special about it – apart from the fact that we know – even though we cannot tell from looking at the file – that the properties will set based on the properties files.

  <managed-bean>
    <managed-bean-name>someBean</managed-bean-name>
    <managed-bean-class>nl.amis.someapp.view.MyBean</managed-bean-class>
    <managed-bean-scope>session</managed-bean-scope>
    <managed-property>
      <property-name>status</property-name>
      <property-class>java.lang.String</property-class>
      <value>#{status}</value>
    </managed-property>
    <managed-property>
      <property-name>greeting</property-name>
      <property-class>java.lang.String</property-class>
      <value>#{greeting}</value>
    </managed-property>
    <managed-property>
      <property-name>companyName</property-name>
      <property-class>java.lang.String</property-class>
      <value>#{companyName}</value>
    </managed-property>
  </managed-bean>

Using the managed bean properties in a JSF page

A simple JSF page is created – with various EL Expresions:

Image

In addition to the EL expressions that refer to someBean, there are also two EL expressions in the page that do not go through someBean: #{yearOfConstruction} and #{country}. These EL expressions cannot be resolved using the normal EL resolving chain. However, since they correspond to an entry in one of the property files and to a system property, they can still be resolved – thanks to the extension of the EL resolvers.

Running the page

Image

Now we change a system property:

Image

Thereby we instruct the application to not read properties from dev-env.properties but instead from test-env.properties. The effect is seen when we restart the application server and redeploy the application:

Image

Resources

Download JDeveloper Application: JsfCustomELPropertiesResolver.