problem upgrading to JSTL 1.2

Y

yishayjobs

Hi All,

We in the process of upgrading our JBoss implementation from version
4.0.3 to 4.2.2. This also implies moving from JSTL 1.1 to JSTL 1.2 and
from Tomcat 5 to Tomcat 6.

Now some of our old JSPs don't work.

For example:

<%-- ${currGroup.current.value} is an ArrayList --%>
<c:set var="messageList" value="${currGroup.current.value.list}"/>

throws the following exception:

javax.el.PropertyNotFoundException: Property 'list' not readable on
type java.util.List

I've found out that changing
${currGroup.current.value.list}
to
#{currGroup.current.value.list} fixes this problem. But I wouldn't
want to make this change all across our JSPs. Besides I thought JSTL
1.2 was supposed to be backwards compatible to JSTL 1.1. That's what
it says here:

http://today.java.net/pub/a/today/2...ression-language.html#backwards-compatibility

Does anyone have an explanation?

Cheers,
Yishay
 
L

Lew

Hi All,

We in the process of upgrading our JBoss implementation from version
4.0.3 to 4.2.2. This also implies moving from JSTL 1.1 to JSTL 1.2 and
from Tomcat 5 to Tomcat 6.

Now some of our old JSPs don't work.

For example:

<%-- ${currGroup.current.value} is an ArrayList --%>
<c:set var="messageList" value="${currGroup.current.value.list}"/>

throws the following exception:

javax.el.PropertyNotFoundException: Property 'list' not readable on
type java.util.List

I've found out that changing
${currGroup.current.value.list}
to
#{currGroup.current.value.list} fixes this problem. But I wouldn't
want to make this change all across our JSPs. Besides I thought JSTL
1.2 was supposed to be backwards compatible to JSTL 1.1. That's what
it says here:

http://today.java.net/pub/a/today/2...ression-language.html#backwards-compatibility

Does anyone have an explanation?

I might, after seeing the definition of the backing bean, particularly the
currGroup object's type, and the declaration of getCurrent(), its type, its
declaration of getValue(), its type in turn, and the declaration of that
type's getList(). The error message indicates that getValue() already returns
a java.util.List(), which of course would not itself have a getList() method.

Incomplete examples lead to incomplete answers.
 
Y

yishayjobs

I might, after seeing the definition of the backing bean, particularly the
currGroup object's type, and the declaration of getCurrent(), its type, its
declaration of getValue(), its type in turn, and the declaration of that
type's getList().  The error message indicates that getValue() already returns
a java.util.List(), which of course would not itself have a getList() method.

Incomplete examples lead to incomplete answers.

Thanks for your quick answer. I'll try to add the missing background.
The expression is read from within a forEach loop in the following
manner:

<c:forEach items="${messageGroup}" varStatus="currGroup">
<c:set var="messageList" value="${currGroup.current.value.list}"/>
<%-- this is irrelevant as it never reaches this spot -->
</c:forEach>

messageGroup is a Map<org.apache.struts.action.GLOBAL_MESSAGE,
org.apache.struts.action.ActionMessages.ActionMessageItem>

So the map is iterated over, currGroup.current.value means
org.apache.struts.action.ActionMessages.ActionMessageItem is accessed,
and currGroup.current.value.list would mean
org.apache.struts.action.ActionMessages.ActionMessageItem.getList() is
called. This method does exist, as I've verified in my java code. It's
also part of the API, as explained in

http://struts.apache.org/1.x/struts...tionMessages.ActionMessageItem.html#getList()

------

Perhaps now you can understand my question. Why does this work:

<c:set var="messageList" value="#{currGroup.current.value.list}"/>

while this doesn't?

<c:set var="messageList" value="${currGroup.current.value.list}"/>
 
L

Lew

Thanks for your quick answer. I'll try to add the missing background.
The expression is read from within a forEach loop in the following
manner:

<c:forEach items="${messageGroup}" varStatus="currGroup">
<c:set var="messageList" value="${currGroup.current.value.list}"/>
<%-- this is irrelevant as it never reaches this spot -->
</c:forEach>

messageGroup is a Map<org.apache.struts.action.GLOBAL_MESSAGE,
org.apache.struts.action.ActionMessages.ActionMessageItem>

So the map is iterated over, currGroup.current.value means
org.apache.struts.action.ActionMessages.ActionMessageItem is accessed,
and currGroup.current.value.list would mean
org.apache.struts.action.ActionMessages.ActionMessageItem.getList() is
called. This method does exist, as I've verified in my java code. It's
also part of the API, as explained in

http://struts.apache.org/1.x/struts...tionMessages.ActionMessageItem.html#getList()

------

Perhaps now you can understand my question. Why does this work:

<c:set var="messageList" value="#{currGroup.current.value.list}"/>

while this doesn't?

<c:set var="messageList" value="${currGroup.current.value.list}"/>

The c:forEach attribute varStatus is of type
javax.servlet.jsp.jstl.core.LoopTagStatus.

That, in turn, has method getCurrent() that returns Object, which does not
have a getValue() method.

But maybe somehow one environment magically figured out the Object is really a
Map.Entry, so it let you get away with it. It shouldn't have.

So your Entry getValue() returns an ActionMessageItem. That in term has a
getList(). Gotcha.

But the error message you reported says, "javax.el.PropertyNotFoundException:
Property 'list' not readable on type java.util.List". That implies that the
parser is trying to read "value" as a List already. Just out of curiosity,
would it work with just

<c:set var="messageList" value="${currGroup.current.value}" />
?

I think I'd rather use a request or page variable 'values' retrieved from
messageGroup.values(), and

<c:forEach items="${values} var="item" >
<c:set var="messageList" value="${item.list}" />
...
</c:forEach>

Let us know the answer to my question above.
 
Y

yishayjobs

The c:forEach attribute varStatus is of type
javax.servlet.jsp.jstl.core.LoopTagStatus.

That, in turn, has method getCurrent() that returns Object, which does not
have a getValue() method.

But maybe somehow one environment magically figured out the Object is really a
Map.Entry, so it let you get away with it.  It shouldn't have.

So your Entry getValue() returns an ActionMessageItem.  That in term has a
getList().  Gotcha.

But the error message you reported says, "javax.el.PropertyNotFoundException:
Property 'list' not readable on type java.util.List".  That implies that the
parser is trying to read "value" as a List already.  Just out of curiosity,
would it work with just

  <c:set var="messageList" value="${currGroup.current.value}" />
?

I think I'd rather use a request or page variable 'values' retrieved from
messageGroup.values(), and

  <c:forEach items="${values} var="item" >
    <c:set var="messageList" value="${item.list}" />
    ...
  </c:forEach>

Let us know the answer to my question above.
parser is trying to read "value" as a List already. Just out of curiosity,
would it work with just

<c:set var="messageList" value="${currGroup.current.value}" />
?

Yes, that does work.

As for my own question:

I was not correct to say the first instance works; I think it is just
evaluated as a literal, which avoids the exception but still means a
malfunction. I'll have to inspect the variables more closely and see
why java.Util.List is ever accessed.
 
L

Lew

I was not correct to say the first instance works; I think it is just
evaluated as a literal, which avoids the exception but still means a
malfunction. I'll have to inspect the variables more closely and see
why java.Util.List is ever accessed.

java.util.List, surely.
 
Y

yishayjobs

java.util.List, surely.

The following jsp (I named it 'master.jsp') is as simple as I've
gotten it.

=====

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.struts.action.ActionMessage"%>
<%@ page import="org.apache.struts.action.ActionMessages"%>

<%! public class MyActionMessages extends ActionMessages {
public java.util.Map getMessages() {
return messages;
}
}
%>

<%
MyActionMessages actionMessages = new MyActionMessages();
actionMessages.add("prop1", new ActionMessage("one"));
session.setAttribute("my_action_message_item",
actionMessages.getMessages().get("prop1"));
%>
<html>
<body>
<span>this is a test: ${my_action_message_item.list}</span><br>
</body>
</html>

====

The message I get is:

org.apache.jasper.JasperException: An exception occurred processing
JSP page /views/profiler/master.jsp at line 20

...

root cause

javax.el.PropertyNotFoundException: Property 'list' not readable on
type java.util.List
javax.el.BeanELResolver$BeanProperty.read(BeanELResolver.java:259)
javax.el.BeanELResolver$BeanProperty.access$000(BeanELResolver.java:
209)
javax.el.BeanELResolver.getValue(BeanELResolver.java:60)
javax.el.CompositeELResolver.getValue(CompositeELResolver.java:53)
org.apache.el.parser.AstValue.getValue(AstValue.java:97)
org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:
186)

org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextImpl.java:
923)
org.apache.jsp.views.profiler.master_jsp._jspService(master_jsp.java:
77)

==========

This is after the upgrade (see my opening post). Before upgrade the
list is evaluated in the normal toString fasion. I get:

====

this is a test: [one[]]

====

It might be be struts or jstl, I'm not sure. It also might be bad
coding on our side, but as I've mentioned this is not my worry. My
worry is performing the upgrade with minimal cost, given our perhaps
badly written legacy code.
 
L

Lew

The following jsp (I named it 'master.jsp') is as simple as I've
gotten it.

=====

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.struts.action.ActionMessage"%>
<%@ page import="org.apache.struts.action.ActionMessages"%>

<%! public class MyActionMessages extends ActionMessages {
public java.util.Map getMessages() {
return messages;
}
}
%>

<%
MyActionMessages actionMessages = new MyActionMessages();
actionMessages.add("prop1", new ActionMessage("one"));
session.setAttribute("my_action_message_item",
actionMessages.getMessages().get("prop1"));
%>
<html>
<body>
<span>this is a test: ${my_action_message_item.list}</span><br>
</body>
</html>

====

The message I get is:

org.apache.jasper.JasperException: An exception occurred processing
JSP page /views/profiler/master.jsp at line 20

...

root cause

javax.el.PropertyNotFoundException: Property 'list' not readable on
type java.util.List
javax.el.BeanELResolver$BeanProperty.read(BeanELResolver.java:259)
javax.el.BeanELResolver$BeanProperty.access$000(BeanELResolver.java:
209)
javax.el.BeanELResolver.getValue(BeanELResolver.java:60)
javax.el.CompositeELResolver.getValue(CompositeELResolver.java:53)
org.apache.el.parser.AstValue.getValue(AstValue.java:97)
org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:
186)

org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextImpl.java:
923)
org.apache.jsp.views.profiler.master_jsp._jspService(master_jsp.java:
77)

==========

This is after the upgrade (see my opening post). Before upgrade the
list is evaluated in the normal toString fasion. I get:

====

this is a test: [one[]]

The object retrieved via 'my_action_message_item' is the one from the call to
actionMessages.getMessages().get("prop1")

'actionMessages.getMessages()' is a Map, 'messages'. A raw Map.
'messages.get("prop1")' returns an Object. 'Object' does not have a method
'getList()'. Boom. But that's not the error. Somehow the system figured out
what the underlying Map types are. Let's follow that chain.

The retrieval from getMessages() is the protected 'messages' element of the
superclass, a HashMap that maps String to an ArrayList. So the result of
get("prop1") is an ArrayList<ActionMessage>.

You then call getList() on that ArrayList. But ArrayList doesn't have such a
method. Now boom.

The exception is correct behavior.
 
L

Lew

The following jsp (I named it 'master.jsp') is as simple as I've
gotten it.

=====

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.struts.action.ActionMessage"%>
<%@ page import="org.apache.struts.action.ActionMessages"%>

<%! public class MyActionMessages extends ActionMessages {
public java.util.Map getMessages() {
return messages;
}
}
%>

<%
MyActionMessages actionMessages = new MyActionMessages();
actionMessages.add("prop1", new ActionMessage("one"));
session.setAttribute("my_action_message_item",
actionMessages.getMessages().get("prop1"));
%>
<html>
<body>
<span>this is a test: ${my_action_message_item.list}</span><br>
</body>
</html>

Why aren't you simply using <bean:message>?
<http://struts.apache.org/1.x/struts-taglib/tlddoc/bean/message.html>

<bean:message key="prop1" />
 
Y

yishayjobs

The following jsp (I named it 'master.jsp') is as simple as I've
gotten it.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.struts.action.ActionMessage"%>
<%@ page import="org.apache.struts.action.ActionMessages"%>
<%! public class MyActionMessages extends ActionMessages {
        public java.util.Map getMessages() {
            return messages;
        }
}
%>
<%
   MyActionMessages actionMessages = new MyActionMessages();
           actionMessages.add("prop1", new ActionMessage("one"));
           session.setAttribute("my_action_message_item",
actionMessages.getMessages().get("prop1"));
%>
<html>
<body>
<span>this is a test: ${my_action_message_item.list}</span><br>
</body>
</html>

The message I get is:
org.apache.jasper.JasperException: An exception occurred processing
JSP page /views/profiler/master.jsp at line 20

root cause
javax.el.PropertyNotFoundException: Property 'list' not readable on
type java.util.List
   javax.el.BeanELResolver$BeanProperty.read(BeanELResolver.java:259)
   javax.el.BeanELResolver$BeanProperty.access$000(BeanELResolver.java:
209)
   javax.el.BeanELResolver.getValue(BeanELResolver.java:60)
   javax.el.CompositeELResolver.getValue(CompositeELResolver.java:53)
   org.apache.el.parser.AstValue.getValue(AstValue.java:97)
   org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:
186)
org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextIm­pl.java:
923)
   org.apache.jsp.views.profiler.master_jsp._jspService(master_jsp.java:
77)
==========

This is after the upgrade (see my opening post). Before upgrade the
list is evaluated in the normal toString fasion. I get:

this is a test: [one[]]

The object retrieved via 'my_action_message_item' is the one from the call to
actionMessages.getMessages().get("prop1")

'actionMessages.getMessages()' is a Map, 'messages'.  A raw Map.
'messages.get("prop1")' returns an Object.  'Object' does not have a method
'getList()'.  Boom.  But that's not the error.  Somehow the system figured out
what the underlying Map types are.  Let's follow that chain.

The retrieval from getMessages() is the protected 'messages' element of the
superclass, a HashMap that maps String to an ArrayList.  So the result of
get("prop1") is an ArrayList<ActionMessage>.

You then call getList() on that ArrayList.  But ArrayList doesn't have such a
method.  Now boom.

The exception is correct behavior.

Is the result of get("prop1") an ArrayList? API docs support this

http://struts.apache.org/1.2.4/api/org/apache/struts/action/ActionMessages.html#messages

But if you run the following jsp (you're welcome to try it; it's self
contained):

-----------
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.struts.action.ActionMessage"%>
<%@ page import="org.apache.struts.action.ActionMessages"%>

<%! public class MyActionMessages extends ActionMessages {
public java.util.Map getMessages() {
return messages;
}
}
%>

<%
MyActionMessages actionMessages = new MyActionMessages();
actionMessages.add("prop1", new ActionMessage("one"));
Object myObject = actionMessages.getMessages().get("prop1");
session.setAttribute("is_list", new Boolean (myObject
instanceof java.util.List));
session.setAttribute("my_action_message_item", myObject);
%>
<html>
<body>
<span>is list? ${is_list}</span><br>
<span>class name: ${my_action_message_item.class.name}</span><br>
</body>
</html>
---------

You'll get:

---------

is list? false
class name: org.apache.struts.action.ActionMessages$ActionMessageItem

---------
 
L

Lew

The following jsp (I named it 'master.jsp') is as simple as I've
gotten it.
=====
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.struts.action.ActionMessage"%>
<%@ page import="org.apache.struts.action.ActionMessages"%>
<%! public class MyActionMessages extends ActionMessages {
public java.util.Map getMessages() {
return messages;
}
}
%>
<%
MyActionMessages actionMessages = new MyActionMessages();
actionMessages.add("prop1", new ActionMessage("one"));
session.setAttribute("my_action_message_item",
actionMessages.getMessages().get("prop1"));
%>
<html>
<body>
<span>this is a test: ${my_action_message_item.list}</span><br>
</body>
</html>
====
The message I get is:
org.apache.jasper.JasperException: An exception occurred processing
JSP page /views/profiler/master.jsp at line 20
...
root cause
javax.el.PropertyNotFoundException: Property 'list' not readable on
type java.util.List
javax.el.BeanELResolver$BeanProperty.read(BeanELResolver.java:259)
javax.el.BeanELResolver$BeanProperty.access$000(BeanELResolver.java:
209)
javax.el.BeanELResolver.getValue(BeanELResolver.java:60)
javax.el.CompositeELResolver.getValue(CompositeELResolver.java:53)
org.apache.el.parser.AstValue.getValue(AstValue.java:97)
org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:
186)
org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextIm­pl.java:
923)
org.apache.jsp.views.profiler.master_jsp._jspService(master_jsp.java:
77)
==========
This is after the upgrade (see my opening post). Before upgrade the
list is evaluated in the normal toString fasion. I get:
====
this is a test: [one[]]
The object retrieved via 'my_action_message_item' is the one from the call to
actionMessages.getMessages().get("prop1")
'actionMessages.getMessages()' is a Map, 'messages'. A raw Map.
'messages.get("prop1")' returns an Object. 'Object' does not have a method
'getList()'. Boom. But that's not the error. Somehow the system figured out
what the underlying Map types are. Let's follow that chain.

The retrieval from getMessages() is the protected 'messages' element of the
superclass, a HashMap that maps String to an ArrayList. So the result of
get("prop1") is an ArrayList<ActionMessage>.

You then call getList() on that ArrayList. But ArrayList doesn't have such a
method. Now boom.

The exception is correct behavior.

Is the result of get("prop1") an ArrayList? API docs support this

http://struts.apache.org/1.2.4/api/org/apache/struts/action/ActionMessages.html#messages

But if you run the following jsp (you're welcome to try it; it's self
contained):

-----------
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.struts.action.ActionMessage"%>
<%@ page import="org.apache.struts.action.ActionMessages"%>

<%! public class MyActionMessages extends ActionMessages {
public java.util.Map getMessages() {
return messages;
}
}
%>

<%
MyActionMessages actionMessages = new MyActionMessages();
actionMessages.add("prop1", new ActionMessage("one"));
Object myObject = actionMessages.getMessages().get("prop1");
session.setAttribute("is_list", new Boolean (myObject
instanceof java.util.List));
session.setAttribute("my_action_message_item", myObject);
%>
<html>
<body>
<span>is list? ${is_list}</span><br>
<span>class name: ${my_action_message_item.class.name}</span><br>
</body>
</html>
---------

You'll get:

---------

is list? false
class name: org.apache.struts.action.ActionMessages$ActionMessageItem

---------

What is your point here? You've proven that Java cannot decipher the
underlying type the way your container seems to be able to.

The return value of get("prop1") from your custom class is an Object, not a
java.util.List. The results you show are correct.
'actionMessages.getMessages()' is a Map, 'messages'. A raw Map.
'messages.get("prop1")' returns an Object.

*Not* a List.

So the result 'false' is correct.

What's amazing to me is that the container figured out that the object is a
List even though your code does its level best to hide that fact. It should
have crashed before then.
 
Y

yishayjobs

(e-mail address removed) wrote:
The following jsp (I named it 'master.jsp') is as simple as I've
gotten it.
=====
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="org.apache.struts.action.ActionMessage"%>
<%@ page import="org.apache.struts.action.ActionMessages"%>
<%! public class MyActionMessages extends ActionMessages {
        public java.util.Map getMessages() {
            return messages;
        }
}
%>
<%
   MyActionMessages actionMessages = new MyActionMessages();
           actionMessages.add("prop1", new ActionMessage("one"));
           session.setAttribute("my_action_message_item",
actionMessages.getMessages().get("prop1"));
%>
<html>
<body>
<span>this is a test: ${my_action_message_item.list}</span><br>
</body>
</html>
====
The message I get is:
org.apache.jasper.JasperException: An exception occurred processing
JSP page /views/profiler/master.jsp at line 20
...
root cause
javax.el.PropertyNotFoundException: Property 'list' not readable on
type java.util.List
   javax.el.BeanELResolver$BeanProperty.read(BeanELResolver.java:259)
   javax.el.BeanELResolver$BeanProperty.access$000(BeanELResolver.java:
209)
   javax.el.BeanELResolver.getValue(BeanELResolver.java:60)
   javax.el.CompositeELResolver.getValue(CompositeELResolver.java:53)
   org.apache.el.parser.AstValue.getValue(AstValue.java:97)
   org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:
186)
org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate(PageContextIm­­pl.java:
923)
   org.apache.jsp.views.profiler.master_jsp._jspService(master_jsp..java:
77)
==========
This is after the upgrade (see my opening post). Before upgrade the
list is evaluated in the normal toString fasion. I get:
====
this is a test: [one[]]
The object retrieved via 'my_action_message_item' is the one from the call to
actionMessages.getMessages().get("prop1")
'actionMessages.getMessages()' is a Map, 'messages'.  A raw Map.
'messages.get("prop1")' returns an Object.  'Object' does not have a method
'getList()'.  Boom.  But that's not the error.  Somehow the system figured out
what the underlying Map types are.  Let's follow that chain.
The retrieval from getMessages() is the protected 'messages' element of the
superclass, a HashMap that maps String to an ArrayList.  So the result of
get("prop1") is an ArrayList<ActionMessage>.
You then call getList() on that ArrayList.  But ArrayList doesn't have such a
method.  Now boom.
The exception is correct behavior.
Is the result of get("prop1") an ArrayList? API docs support this

But if you run the following jsp (you're welcome to try it; it's self
contained):
<%! public class MyActionMessages extends ActionMessages {
        public java.util.Map getMessages() {
            return messages;
        }
}
%>
<%
   MyActionMessages actionMessages = new MyActionMessages();
           actionMessages.add("prop1", new ActionMessage("one"));
           Object myObject = actionMessages.getMessages().get("prop1");
           session.setAttribute("is_list", new Boolean (myObject
instanceof java.util.List));
           session.setAttribute("my_action_message_item", myObject);
%>
<html>
<body>
<span>is list? ${is_list}</span><br>
<span>class name: ${my_action_message_item.class.name}</span><br>
</body>
</html>
---------
You'll get:

is list? false
class name: org.apache.struts.action.ActionMessages$ActionMessageItem
---------

What is your point here?  You've proven that Java cannot decipher the
underlying type the way your container seems to be able to.

The return value of get("prop1") from your custom class is an Object, not a
java.util.List.  The results you show are correct.
'actionMessages.getMessages()' is a Map, 'messages'.  A raw Map.
'messages.get("prop1")' returns an Object.

*Not* a List.

So the result 'false' is correct.

What's amazing to me is that the container figured out that the object is a
List even though your code does its level best to hide that fact.  It should
have crashed before then.

If the container figure out the object is a list then why does
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,185
Members
46,737
Latest member
Georgeengab

Latest Threads

Top