How to reference constants in EL?
Asked Answered
R

12

110

How do you reference an constants with EL on a JSP page?

I have an interface Addresses with a constant named URL. I know I can reference it with a scriplet by going: <%=Addresses.URL%>, but how do I do this using EL?

Rosebay answered 17/9, 2010 at 4:10 Comment(0)
U
162

EL 3.0 or newer

If you're already on Java EE 7 / EL 3.0, then the @page import will also import class constants in EL scope.

<%@ page import="com.example.YourConstants" %>

This will under the covers be imported via ImportHandler#importClass() and be available as ${YourConstants.FOO}.

Note that all java.lang.* classes are already implicitly imported and available like so ${Boolean.TRUE} and ${Integer.MAX_VALUE}. This only requires a more recent Java EE 7 container server as early versions had bugs in this. E.g. GlassFish 4.0 and Tomcat 8.0.0-1x fails, but GlassFish 4.1+ and Tomcat 8.0.2x+ works. And you need to make absolutely sure that your web.xml is declared conform the latest servlet version supported by the server. Thus with a web.xml which is declared conform Servlet 2.5 or older, none of the Servlet 3.0+ features will work.

Also note that this facility is only available in JSP and not in Facelets. In case of JSF+Facelets, your best bet is using OmniFaces <o:importConstants> as below:

<o:importConstants type="com.example.YourConstants" />

Or adding an EL context listener which calls ImportHandler#importClass() as below:

@ManagedBean(eager=true)
@ApplicationScoped
public class Config {

    @PostConstruct
    public void init() {
        FacesContext.getCurrentInstance().getApplication().addELContextListener(new ELContextListener() {
            @Override
            public void contextCreated(ELContextEvent event) {
                event.getELContext().getImportHandler().importClass("com.example.YourConstants");
            }
        });
    }

}

EL 2.2 or older

This is not possible in EL 2.2 and older. There are several alternatives:

  1. Put them in a Map<String, Object> which you put in the application scope. In EL, map values are accessible the usual Javabean way by ${map.key} or ${map['key.with.dots']}.

  2. Use <un:useConstants> of the Unstandard taglib (maven2 repo here):

    <%@ taglib uri="http://jakarta.apache.org/taglibs/unstandard-1.0" prefix="un" %>
    <un:useConstants className="com.example.YourConstants" var="constants" />
    

    This way they are accessible the usual Javabean way by ${constants.FOO}.

  3. Use Javaranch's CCC <ccc:constantsMap> as desribed somewhere at the bottom of this article.

    <%@ taglib uri="http://bibeault.org/tld/ccc" prefix="ccc" %>
    <ccc:constantsMap className="com.example.YourConstants" var="constants" />
    

    This way they are accessible the usual Javabean way by ${constants.FOO} as well.

  4. If you're using JSF2, then you could use <o:importConstants> of OmniFaces.

    <html ... xmlns:o="http://omnifaces.org/ui">
    <o:importConstants type="com.example.YourConstants" />
    

    This way they are accessible the usual Javabean way by #{YourConstants.FOO} as well.

  5. Create a wrapper class which returns them through Javabean-style getter methods.

  6. Create a custom EL resolver which first scans the presence of a constant and if absent, then delegate to the default resolver, otherwise returns the constant value instead.

Understrapper answered 17/9, 2010 at 11:46 Comment(13)
I found this question because I was having the same problem when trying to use a static List field with a form:options tag. I was able to get it working by adding a non-static getter that returns the static list. It's a little kludgy but hey, that's JSP development for ya!Vestavestal
Do you have any example how to configure this for JSF if the beans are managed by spring? Thx in advance.Straightway
@Lodger: I don't do Spring.Understrapper
Is the jakarta unstandard-taglib project still alive? is there some alternative?Timer
Is there a way to leverage this techniques for enums?Maternity
@Niklas: enums are also constants.Understrapper
@Understrapper Yesterday I tried it with enum MyEnum and inserted <@page import="myPackage.MyEnum" %>. Then ${MyEnum.MEMBER} still returned empty string, whereas importing constants works. I use Tomcat 8.0.28 and Java 8. With scriptlets of course it does work ...Maternity
Concerning the EL 3.0 JSF variant, a bug was submitted for Mojarra 2.2+ and fixed for future version Mojarra 2.3: java.net/jira/browse/JAVASERVERFACES-4189 - This bug prevents class/constants resolution in JSTL tags like <c:if test="#{val eq MyEnum.CONST}"/>Warnerwarning
If I open a bounty on this question #44518643 can you help to solve the problem?Figueroa
Note that this only works for concrete classes. docs.oracle.com/javaee/7/api/javax/el/ImportHandler.html states that the class must not be abstract or an interface. This appears to be more of a stylistic assertion than a technical limitation, but reference implementations enforce it.Cowslip
I'm not seeing this working. I have a class with constants I'm trying to reference. I.e. MyClass.MY_CONST. The value of MY_CONST in this case is an array of the enum values, i.e. public static final MyEnum[] MY_CONST = { MyEnum.VAL1, MyEnum.VAL2}Indiction
@Ryan: You're referring to the EL 3.0 approach? That can happen when your server or web.xml is not EL 3.0 compatible.Understrapper
@BalusC, yes, I am talking about the EL 3.0 methodology. I'm using Wildfly 12 at the moment.Indiction
S
11

The following does not apply to EL in general, but instead to SpEL (Spring EL) only (tested with 3.2.2.RELEASE on Tomcat 7). I think it is worth mentioning it here in case someone searches for JSP and EL (but uses JSP with Spring).

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<spring:eval var="constant" expression="T(com.example.Constants).CONSTANT"/>
Sf answered 21/6, 2013 at 16:12 Comment(0)
G
9

You usually place these kinds of constants in a Configuration object (which has getters and setters) in the servlet context, and access them with ${applicationScope.config.url}

Godin answered 17/9, 2010 at 4:27 Comment(3)
Bit of a novice here when it comes to jsp's- could you explain that more fully?Rosebay
@tau-neutrino: Its simple actually. Create a class with url as a String property, name it Configuration, instantiate it and set the url to whatever you like. After that set that Configuration object in ServletContext. Do something like, servletContext.setAttribute("config", config). And there you go.Sassaby
What's the difference between your proposed solution and simply adding the constant as an attribute of the ServletContext? Is it just that you can classify the constants more neatly? eg: applicationScope.config.url vs applicationScope.url.Bashibazouk
S
8

You can't. It follows the Java Bean convention. So you must have a getter for it.

Sassaby answered 17/9, 2010 at 4:20 Comment(0)
B
5

Static properties aren't accessible in EL. The workaround I use is to create a non-static variable which assigns itself to the static value.

public final static String MANAGER_ROLE = 'manager';
public String manager_role = MANAGER_ROLE;

I use lombok to generate the getter and setter so that's pretty well it. Your EL looks like this:

${bean.manager_role}

Full code at https://rogerkeays.com/access-java-static-methods-and-constants-from-el

Better answered 16/7, 2012 at 20:54 Comment(0)
S
5

I implemented like:

public interface Constants{
    Integer PAGE_SIZE = 20;
}

-

public class JspConstants extends HashMap<String, String> {

        public JspConstants() {
            Class c = Constants.class;
            Field[] fields = c.getDeclaredFields();
            for(Field field : fields) {
                int modifier = field.getModifiers();
                if(Modifier.isPublic(modifier) && Modifier.isStatic(modifier) && Modifier.isFinal(modifier)) {
                    try {
                        Object o = field.get(null);
                        put(field.getName(), o != null ? o.toString() : null);
                    } catch(IllegalAccessException ignored) {
                    }
                }
            }
        }

        @Override
        public String get(Object key) {
            String result = super.get(key);
            if(StringUtils.isEmpty(result)) {
                throw new IllegalArgumentException("Check key! The key is wrong, no such constant!");
            }
            return result;
        }
    }

Next step put instance of this class into servlerContext

public class ApplicationInitializer implements ServletContextListener {


    @Override
    public void contextInitialized(ServletContextEvent sce) {
        sce.getServletContext().setAttribute("Constants", new JspConstants());
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }
}

add listener to web.xml

<listener>
    <listener-class>com.example.ApplicationInitializer</listener-class>
</listener>

access in jsp

${Constants.PAGE_SIZE}
Scoter answered 25/6, 2013 at 15:18 Comment(0)
M
5

I'm defining a constant in my jsp right at the beginning:

<%final String URI = "http://www.example.com/";%>

I include the core taglib in my JSP:

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

Then, I make the constant available to EL by following statement:

<c:set var="URI" value="<%=URI%>"></c:set>

Now, I can use it later. Here an example, where the value is just written as HTML comment for debugging purposes:

<!-- ${URI} -->

With your constant class, you can just import your class and assign the constants to local variables. I know that my answer is a sort of quick hack, but the question also bumps up when one wants to define constants directly in the JSP.

Manas answered 21/11, 2013 at 12:11 Comment(4)
lol why not use Scriptlets directly if that's how you initialize the EL variables?Alberto
Three lines on the top being a mess and then clean EL throughout the rest of the JSP ^^.Manas
@koppoor I guess so. I'm just going to use <%=URI%> :PAlberto
I had a place where a direct <%=URI%> did not work, but this technique did.Berns
H
3

Yes, you can. You need a custom tag (if you can't find it somewhere else). I've done this:

package something;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

import org.apache.taglibs.standard.tag.el.core.ExpressionUtil;

/**
 * Get all class constants (statics) and place into Map so they can be accessed
 * from EL.
 * @author Tim.sabin
 */
public class ConstMapTag extends TagSupport {
    public static final long serialVersionUID = 0x2ed23c0f306L;

    private String path = "";
    private String var = "";

    public void setPath (String path) throws JspException {
        this.path = (String)ExpressionUtil.evalNotNull ("constMap", "path",
          path, String.class, this, pageContext);
    }

    public void setVar (String var) throws JspException {
        this.var = (String)ExpressionUtil.evalNotNull ("constMap", "var",
          var, String.class, this, pageContext);
    }

    public int doStartTag () throws JspException {
        // Use Reflection to look up the desired field.
        try {
            Class<?> clazz = null;
            try {
                clazz = Class.forName (path);
            } catch (ClassNotFoundException ex) {
                throw new JspException ("Class " + path + " not found.");
            }
            Field [] flds = clazz.getDeclaredFields ();
            // Go through all the fields, and put static ones in a Map.
            Map<String, Object> constMap = new TreeMap<String, Object> ();
            for (int i = 0; i < flds.length; i++) {
                // Check to see if this is public static final. If not, it's not a constant.
                int mods = flds [i].getModifiers ();
                if (!Modifier.isFinal (mods) || !Modifier.isStatic (mods) ||
                  !Modifier.isPublic (mods)) {
                    continue;
                }
                Object val = null;
                try {
                    val = flds [i].get (null);    // null for static fields.
                } catch (Exception ex) {
                    System.out.println ("Problem getting value of " + flds [i].getName ());
                    continue;
                }
                // flds [i].get () automatically wraps primitives.
                // Place the constant into the Map.
                constMap.put (flds [i].getName (), val);
            }
            // Export the Map as a Page variable.
            pageContext.setAttribute (var, constMap);
        } catch (Exception ex) {
            if (!(ex instanceof JspException)) {
                throw new JspException ("Could not process constants from class " + path);
            } else {
                throw (JspException)ex;
            }
        }
        return SKIP_BODY;
    }
}

and the tag is called:

<yourLib:constMap path="path.to.your.constantClass" var="consts" />

All public static final variables will be put into a Map indexed by their Java name, so if

public static final int MY_FIFTEEN = 15;

then the tag will wrap this in an Integer and you can reference it in a JSP:

<c:if test="${consts['MY_FIFTEEN'] eq 15}">

and you don't have to write getters!

Heida answered 15/3, 2012 at 19:26 Comment(0)
S
3

You can. Try in follow way

 #{T(com.example.Addresses).URL}

Tested on TomCat 7 and java6

Spellbound answered 9/9, 2013 at 10:8 Comment(1)
That looks like SpEL, and not EL. Am I mistaken? Also, would that work in an older Tomcat5.5?Earthiness
O
2

Even knowing its a little late, and even knowing this is a little hack - i used the following solution to achieve the desired result. If you are a lover of Java-Naming-Conventions, my advice is to stop reading here...

Having a class like this, defining Constants, grouped by empty classes to create kind of a hierarchy:

public class PERMISSION{
    public static class PAGE{
       public static final Long SEE = 1L; 
       public static final Long EDIT = 2L; 
       public static final Long DELETE = 4L; 
       ...
    }
}

can be used from within java as PERMISSION.PAGE.SEE to retrieve the value 1L

To achieve a simliar access-possibility from within EL-Expressions, I did this: (If there is a coding-god - he hopefully might forgive me :D )

@Named(value="PERMISSION")
public class PERMISSION{
    public static class PAGE{
       public static final Long SEE = 1L; 
       public static final Long EDIT = 2L; 
       public static final Long DELETE = 4L; 
       ...

       //EL Wrapper
       public Long getSEE(){
           return PAGE.SEE;
       }

       public Long getEDIT(){
           return PAGE.EDIT;
       }

       public Long getDELETE(){
           return PAGE.DELETE;
       }
    }

    //EL-Wrapper
    public PAGE getPAGE() {
        return new PAGE();
    }
}

finally, the EL-Expression to access the very same Long becomes: #{PERMISSION.PAGE.SEE} - equality for Java and EL-Access. I know this is out of any convention, but it works perfectly fine.

Overjoy answered 29/12, 2014 at 21:31 Comment(0)
B
2

@Bozho already provided a great answer

You usually place these kinds of constants in a Configuration object (which has getters and setters) in the servlet context, and access them with ${applicationScope.config.url}

However, I feel an example is needed so it brings a bit more clarity and spare someone's time

@Component
public Configuration implements ServletContextAware {
    private String addressURL = Addresses.URL;

    // Declare other properties if you need as also add corresponding
    // getters and setters

    public String getAddressURL() {
        return addressURL;
    }

    public void setServletContext(ServletContext servletContext) {
        servletContext.setAttribute("config", this);
    }
}
Bollix answered 6/3, 2015 at 13:22 Comment(0)
G
0

There is a workaround that is not exactly what you want, but lets you active almost the same with touching scriptlets in a quite minimal way. You can use scriptlet to put value into a JSTL variable and use clean JSTL code later in the page.

<%@ taglib prefix="c"       uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="com.whichever.namespace.Addresses" %>
<c:set var="ourUrl" value="<%=Addresses.URL%>"/>
<c:if test='${"http://www.google.com" eq ourUrl}'>
   Google is our URL!
</c:if>
Ganymede answered 17/8, 2016 at 10:29 Comment(1)
I don't see why this was down-voted. This is the same pattern as option #3 in: https://mcmap.net/q/196412/-access-enum-value-using-el-with-jstlEvette

© 2022 - 2024 — McMap. All rights reserved.