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!)
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With