usr-local-ΕΨΗΕΛΩΝ
usr-local-ΕΨΗΕΛΩΝ

Reputation: 26874

EL expression for class

I don't understand how to represent a Class in EL expressions.

I have an EL function that takes a class as parameter (namely an Enum class) to return possible enum values.

I want to invoke it as EL expression. E.g. ${myTld:enumer(com.example.enums.MyEnum)}

However:

How do I express a class in EL without possibly passing by its string representation?

The below works

<function>
    <description>
        Returns the list of enum values for the given enum class
    </description>
    <name>enumer</name>
    <function-class>com.example.Functions</function-class>
    <function-signature>List enumer(java.lang.String)</function-signature>
</function>

${tld:enumer('com.example.MyEnum')}

What I would like

    <function-signature>List enumer(java.lang.Class)</function-signature>

Upvotes: 0

Views: 1242

Answers (2)

BalusC
BalusC

Reputation: 1108742

You can't represent a Class in plain EL. You can at most pass it through if it were a bean property like so ${myTld:enumer(bean.someEnumClass)} returning Class<? extends Enum>, or ${myTld:enumer(bean.someEnum['class'])} where someEnum is the actual Enum, but that's it. Passing around the class name as String is really your best bet.

If you're already on EL 3.0 (available on Servlet 3.1 containers like Tomcat 8, WildFly 8, etc), an alternative to your custom function is to just import it right away in JSP like below using the new EL 3.0 ImportHandler API:

${pageContext.ELContext.importHandler.importClass('com.example.enums.MyEnum')}

The enum is then available by ${MyEnum}.

<c:forEach items="${MyEnum.values()}" var="myEnumValue">
    ${myEnumValue}<br/>
</c:forEach>

Alternatively, import the ${MyEnum} globally via a servlet context listener like below:

@WebListener
public class Config implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent event) {
        JspFactory.getDefaultFactory().getJspApplicationContext(event.getServletContext()).addELContextListener(new ELContextListener() {
            @Override
            public void contextCreated(ELContextEvent event) {
                event.getELContext().getImportHandler().importClass(MyEnum.class.getName());
            }
        });
    }

    // ...
}

Upvotes: 3

Jeff Morin
Jeff Morin

Reputation: 1010

I guess you wish to pass Class arguments to your function to improve performance by avoid parsing strings with Class.forName() each time... True?

If so, I think there is a way... If you know in advance the classes you will pass your function as arguments, use a ServletContextListener to put them into your application scope. This may look as follows:

@WebListener
public class MyEnumInitializer implements ServletContextListener {

  public void contextInitialized(ServletContextEvent sce) {
    Map<String, Class<?>> map = new HashMap<>();
    map.put("MyEnum1", MyEnum1.class);
    map.put("MyEnum2", MyEnum2.class);
    // etc.
    sce.getServletContext().addAttribute("myEnums", map);
  }

  public void contextDestroyed(ServletContextEvent sce) {}

}

If you are using a Servlet API version prior to 3.0, discard the @WebListener annotation above and declare the listener in your web.xml file as follows:

<listener>
  <listener-class>mypackage.MyEnumListener</listener-class>
</listener>

Having your function that takes a Class, you could use it as follows:

${tld:enumer(myEnums['MyEnum1'])}

There is still a String-to-Class mapping, but it involves locating a Class object using a String key in a HashMap, which is faster than resolving the Class object using reflection.

Moreover, if you ever need to add to this HashMap a class you can't foresee as of initializing the application, you can still do it wherever you have access to your ServletContext (the HashMapisn't immutable). For example, in any method receiving an HttpServletRequest as a parameter:

public void aMethod(HttpServletRequest req, [other params]) {
  // ...
  Map<String, Class<?>> myEnums = (Map<String, Class<?>>) req.getSession().getServletContext().getAttribute("myEnums");
  if (!myEnums.containsKey("aNewEnum")) {
    myEnums.put("aNewEnum", NewEnum.class);
  }
  // ...
}

Cheers,

Jeff

Upvotes: 3

Related Questions