[GLASSFISH-20778] EL 3.0 doesn't behave in Glassfish 4 the same way as in the standalone API Created: 23/Aug/13  Updated: 29/Jan/15  Resolved: 26/Aug/13

Status: Resolved
Project: glassfish
Component/s: web_container
Affects Version/s: 4.0
Fix Version/s: None

Type: Bug Priority: Major
Reporter: marcelocenerine Assignee: kchung
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

windows 7, Dell Inspiron


Tags: EL, jsp

 Description   

While writing some examples using the new features of Expression Language 3.0 (both in JSP and using the standalone API) I noticed that some of the new stuff are not behaving the same way in both places:

1- According to item 1.22 of EL spec, "A static field or static method of a Javaclass can be referenced with the syntax classname.field, such as Boolean.TRUE".
Doing so in the standalone API, it works as expected:

ELProcessor elp = new ELProcessor();
Object ret = elp.eval("Boolean.TRUE");
System.out.println("Output Value: " + ret);

Resulting: "Output Value: true"

or...

ELProcessor elp = new ELProcessor();
Object ret = elp.eval("(x -> Math.sqrt)(9)");
System.out.println("Output Value: " + ret);

prints: 9.0

However, when doing the same thing in JSP, nothing is printed:

$

{Boolean.TRUE}

$

{Integer.MAX_VALUE}

$

{Boolean.TRUE == true}

// prints 'false'
$

{(x -> Math.sqrt(x))(9)}

2 - According to item 1.22.3 of EL spec, "A class name reference, followed by arguments in parenthesis, such as Boolean(true) denotes the invocation of the constructor of the class with the supplied arguments."
Doing so using the standalone API:

ELProcessor elp = new ELProcessor();
Object ret = elp.eval("StringBuilder('Java EE rocks')");
System.out.println("Output Value: " + ret);

Prints: Output Value: Java EE rocks

But, in JSP:

$

{StringBuilder('Java EE rocks')}

Results:

exception
org.apache.jasper.JasperException: javax.el.ELException: Expression uses
functions, but no FunctionMapper was provided root cause

*javax.el.ELException: Expression uses functions, but no FunctionMapper was
provided*

These expressions I have mentioned above wouldn't be supposed to work the same way in JSP and in the standalone API?



 Comments   
Comment by kchung [ 26/Aug/13 ]

Hmm... Looks like the java.lang language is properly imported in stand-alone EL, but not in EL used by JSP container. Will take a look.

Comment by kchung [ 26/Aug/13 ]

The JSP api was fixed.

The maven artifact (javax.servlet.jsp: javax.servlet.jsp-api version 2.3.2-b01) to be published and integrated to glassfish v4.

The fix is also available from the source in svn repo https://svn.java.net/svn/jsp~svn/trunk/api (revision 1459). The jar file can be built with a "mvn install", and the resultant jar file can be integrated with glassfish v4 by copying it to <gf>/modules directory, renaming (and replacing) javax.servlet.jsp-api.jar

Comment by balusc [ 26/Aug/13 ]

Great work.

Please don't forget to implement this on Facelets as well and to update https://java.net/projects/el-spec/pages/StaticField on the subject. This confused a lot of users about the correct syntax of referencing constants in EL. It was mentioned in EL 3.0 EDR, however it was modified in EL 3.0 FR.

Comment by kchung [ 27/Aug/13 ]

I've updated JSP source with a simpler fix.

The call to the constructor of an imported class fail due to a bug in EL. This is also fixed. a new EL jar will be published and integrated into glassfish v4.

Comment by kchung [ 27/Aug/13 ]

For the benefit of other container using EL 3.0, here's a description of the cause for the problem, and how it is fixed in JSP.

In a JSP container, the method ScopedAttributeELResolver.getValue(Object base, Object property) always sets the field ELResolver.propertyResolver to true if the base is null. This is a problem for EL, because only checks that a name is actually an imported class if the name is not resolved by a ELResolver. The fix is to check if the name is an imported class in ScopedAttributeELResolver.

Here's the method in JSP:


package javax.servlet.jsp.el;

public class ScopedAttributeELResolver extends ELResolver {
    ...
    public Object getValue(ELContext context,
                           Object base,
                           Object property) {

        if (context == null) {
            throw new NullPointerException();
        }

        if (base == null) {
            context.setPropertyResolved(true);
            if (property instanceof String) {
                String attribute = (String) property;
                PageContext ctxt = (PageContext)
                                       context.getContext(JspContext.class);
                Object value = ctxt.findAttribute(attribute);
                // To support reference of static fields for imported class in
                // EL 3.0, if a scoped attribute returns null, this attribute
                // is further checked to see if it is the name of an imported
                // class.  If so, an ELClass instance is returned.
                // Note: the JSP spec needs to be updated for this behavior. Note
                // also that this behavior is not backward compatible with JSP 2.2
                // and a runtime switch may be needed to force backward
                // compatility.
                if (value == null) {
                    // check to see if the property is an imported class
                    if (context.getImportHandler() != null) {
                        Class<?> c = context.getImportHandler().resolveClass(attribute);
                        if (c != null) {
                            value = new ELClass(c);
                            // A possible optimization is to set the ELClass
                            // instance in an attribute map.
                        }
                    }
                }
                return value;
            }
        }
        return null;
    }
...
}

Comment by marcelocenerine [ 03/Sep/13 ]

I've built and installed a new version of javax.servlet.jsp-api-2.3.2-b01-SNAPSHOT.jar (based on revision 1460) as explained by kchung. The issues related to accessing static members have been solved. However, constructor invocation doesn't seem to be working. I got the same error:

 
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Expression Language 3.0: Miscelaneous</title>
    </head>
    <body>
        ${Boolean(false)}
    </body>
</html>

ERROR PAGE:

type Exception report
messageInternal Server Error
descriptionThe server encountered an internal error that prevented it from fulfilling this request.
exception
org.apache.jasper.JasperException: javax.el.ELException: Expression uses functions, but no FunctionMapper was provided
root cause
javax.el.ELException: Expression uses functions, but no FunctionMapper was provided

GLASSFISH LOG:

WARNING: StandardWrapperValve[jsp]: Servlet.service() for servlet jsp threw exception
javax.el.ELException: Expression uses functions, but no FunctionMapper was provided
at com.sun.el.parser.AstFunction.getValue(AstFunction.java:161)
at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:226)
at org.jboss.weld.el.WeldValueExpression.getValue(WeldValueExpression.java:50)
at org.apache.jasper.runtime.PageContextImpl.evaluateExpression(PageContextImpl.java:1016)
at org.apache.jsp.newFeatures.miscelaneous_jsp._jspService(miscelaneous_jsp.java:56)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:111)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:411)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:473)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:377)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:318)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Thread.java:722)

Should I update/install any other jar?

Thanks

Comment by kchung [ 03/Sep/13 ]

The failure to invoke the constructor of an import class was fixed in the EL. You'll need to build it from the repository https://svn.java.net/svn/uel~svn/trunk, copy javax.el-3.0.1-b01-SNAPSHOT.jar to <gf>/modules, and renaming (and replacing) javax.el.jar.

Comment by sartoris [ 29/Jan/15 ]

Hello, I realize this issue has been resolved. I see that the JSP API fix is in the JSP 2.3.2-b01 Beta version. Are there any plans to move this from a Beta version to a normal update version?

Generated at Wed Apr 01 06:17:38 UTC 2015 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.