Reputation: 307
I am creating a simple guest book in JSP in order to learn this technology. Currently I have two classes: guestbook/GuestBook.class and guestbook/Entry.class (I haven't finished the app yet, so I have only these classes) which are added into WEB-INF/libs/ and they're included properly. In my file index.jsp I am using guestbook.GuestBook class; its method returns Vector. When I iterate over entries and I'd like to print an author of the entry, I can see:
javax.el.PropertyNotFoundException: Property 'author' not found on type guestbook.Entry
I must add that Entry class is public and author attribute is declared in such way:
public String author;
So it is public, too. This is my code when I iterate over the entries:
<c:forEach items="${entries}" varStatus="i">
<c:set var="entry" value="${entries[i.index]}" />
<li><c:out value="${entry.author}" /></li>
</c:forEach>
and
entry.class.name
returns guestbook.Entry
The classes are in package guestbook (as you can guess), entries vector is passed to pageContext.
I do not know what is wrong with my way of doing it. Can anybody help me please with that? (Thanks in advance!)
Upvotes: 10
Views: 8827
Reputation: 360
I had a problem in Build Path. javax.servlet.jsp.jstl-1.2.1.jar was deleted but not removed from Build Path. After removing it from Build Path Property 'xxx' not found issue disappeared.
Upvotes: 0
Reputation: 18562
You can also modify the EL-resolver to access public fields if a getter is not found. To do this, you first need to create your special ELResolver:
public class PublicFieldSupportingELResolver extends ELResolver {
@Override
public Class<?> getCommonPropertyType(ELContext context, Object base) {
return null;
}
@Override
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
return null;
}
@Override
public Class<?> getType(ELContext context, Object base, Object property) {
return null;
}
@Override
public Object getValue(ELContext context, Object base, Object property) {
try {
return context.getELResolver().getValue(
context, base, property);
} catch(RuntimeException ex) {
if(property instanceof String && base != null) {
try {
Field field = base.getClass().getDeclaredField((String) property);
Object value = field.get(base);
context.setPropertyResolved(true);
return value;
} catch (Exception e) {
throw new PropertyNotFoundException(e);
}
} else {
throw ex;
}
}
}
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
return false;
}
@Override
public void setValue(ELContext context, Object base, Object property, Object value) {
}
}
Then you need a class to help you configure it:
public class PublicFieldSupportingELResolverConfigurer implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
JspFactory.getDefaultFactory()
.getJspApplicationContext(event.getServletContext())
.addELResolver(new PublicFieldSupportingELResolver());
}
public void contextDestroyed(ServletContextEvent event) {
}
}
At last you need to run this configurer-class when the servlet starts up. Do this by adding this class as a servlet listener in your web.xml:
<listener>
<listener-class>your.package.PublicFieldSupportingELResolverConfigurer</listener-class>
</listener>
Now you can refer to public fields in your JSPs.
Upvotes: 4
Reputation: 403601
JSP EL will not recognise public fields in your classes, it only works with getter methods (which is good practice anyway - never expose your classes' state as public fields like this).
So use
private String author;
public String getAuthor() {
return author;
}
instead of
public String author;
As a side note, your JSTL is overly complicated, it can be simplified to:
<c:forEach items="${entries}" var="entry">
<li><c:out value="${entry.author}" /></li>
</c:forEach>
or even
<c:forEach items="${entries}" var="entry">
<li>${entry.author}</li>
</c:forEach>
although the latter form will not XML-escape the author name, so isn't advised.
Lastly, the Vector
class is obsolete, you should use ArrayList
instead.
Upvotes: 12