Consider the following interface:
public interface I {
default String getProperty() {
return "...";
}
}
and the implementing class which just re-uses the default implementation:
public final class C implements I {
// empty
}
Whenever an instance of C
is used in JSP EL scripting context:
<jsp:useBean id = "c" class = "com.example.C" scope = "request"/>
${c.property}
-- I receive a PropertyNotFoundException
:
javax.el.PropertyNotFoundException: Property 'property' not found on type com.example.C
javax.el.BeanELResolver$BeanProperties.get(BeanELResolver.java:268)
javax.el.BeanELResolver$BeanProperties.access$300(BeanELResolver.java:221)
javax.el.BeanELResolver.property(BeanELResolver.java:355)
javax.el.BeanELResolver.getValue(BeanELResolver.java:95)
org.apache.jasper.el.JasperELResolver.getValue(JasperELResolver.java:110)
org.apache.el.parser.AstValue.getValue(AstValue.java:169)
org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:184)
org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextImpl.java:943)
org.apache.jsp.index_jsp._jspService(index_jsp.java:225)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:438)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:396)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:340)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
My initial idea Tomcat 6.0 was too old for Java 1.8 features, but I was surprised to see Tomcat 8.0 is also affected. Of course I can work the issue around by calling the default implementation explicitly:
@Override
public String getProperty() {
return I.super.getProperty();
}
-- but why on earth a default method could be a problem for Tomcat?
Update: further testing reveals default properties can't be found, while default methods can, so another workaround (Tomcat 7+) is:
<jsp:useBean id = "c" class = "com.example.C" scope = "request"/>
<%-- ${c.property} --%>
${c.getProperty()}
You can work around this by creating a custom
ELResolver
implementation which handles default methods. The implementation I have made here extendsSimpleSpringBeanELResolver
. This is Springs implementation ofELResolver
but the same idea should be the same without Spring.This class looks for bean property signatures defined on interfaces of the bean and attempts to use them. If no bean prop signature was found on an interface it continues on sending it down the chain of default behavior.
You will then need to register your
ELResolver
in your application somewhere. In my case I am using Spring's java configuration so I have the following:Im not 100% sure on if thats the appropriate place to add our resolver but it does work just fine. You can also load the ELResolver in during
javax.servlet.ServletContextListener.contextInitialized
Here is the
ELResolver
in reference: http://docs.oracle.com/javaee/7/api/javax/el/ELResolver.html