Issue
I am running into a common issue with JSP not displaying data. However, I am following documentation and have also tried the solutions to the many questions that have been asked on this site without any success.
How can I display java.io.File data in JSP? If I am doing so correctly, is there any additional configuration required?
I have a File[] using java.io.File which is created in a javax.servlet.http.HttpServlet. I then take the File[] and add it as an attribute to my HttpSession. This is then forwarded to the JSP file, passed to a forEach tag, and populated into a table. (see this article)
This is my java servlet:
public class LogManagement extends HttpServlet {
private static final Logger logger = LoggerFactory.getLogger(LogManagement.class);
private static final String BASE_DIRECTORY = System.getProperty("catalina.base");
private static final File CURRENT_LOGS_FOLDER = new File(BASE_DIRECTORY + "/logs");
private static final File CAPTURED_LOGS_FOLDER = new File(BASE_DIRECTORY + "/logs/captured");
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
logger.info("GET called on LogManagement servlet");
try {
getLogs(request);
getServletContext().getRequestDispatcher("/LogManagement.jsp").forward(request, response);
} catch (Exception e) {
logger.info("ERROR unable to GET log data");
logger.info(e.getMessage());
}
}
private void getLogs(HttpServletRequest request) {
logger.info("Getting logs");
File[] currentLogs = CURRENT_LOGS_FOLDER.listFiles();
File[] capturedLogs = CAPTURED_LOGS_FOLDER.listFiles();
HttpSession session = request.getSession();
session.setAttribute("currentLogs", currentLogs);
session.setAttribute("capturedLogs", capturedLogs);
logger.info("Got logs");
}
}
This is my jsp for two of the solutions I tried from here and here (yes I know it uses some deprecated tag attributes, but I am unable to change those)
Solution 1:
<table style="margin-top: 5px; width: 600px;" align="left">
<tr>
<td align="left">
Current Logs:
</td>
</tr>
<tr>
<td>
<div class="file-viewer">
<table>
<c:forEach var="file" items="${currentLogs}">
<tr>
<td><c:out value="${file.name}"/></td>
<td><c:out value="${file.length}"/></td>
<td><c:out value="${file.lastModified}"/></td>
</tr>
</c:forEach>
</table>
</div>
</td>
</tr>
</table>
Solution 2:
<table style="margin-top: 5px; width: 600px;" align="left">
<tr>
<td align="left">
Captured Logs:
</td>
</tr>
<tr>
<td>
<div class="file-viewer">
<table>
<c:forEach var="file" items="${capturedLogs}">
<tr>
<td>${file.name}</td>
<td>${file.length}</td>
<td>${file.lastModified}</td>
</tr>
</c:forEach>
</table>
</div>
</td>
</tr>
</table>
These are the two solutions I have seen in documentation and in various SO answers. I have also tried <% out.print(file.getName()); %>
inside of the td tag with no luck (see unable to display jsp data in table)
This is the exception that I get:
An exception occurred processing [/LogManagement.jsp] at line [51]__48: ________c:forEach var=file items=${currentLogs}49: _____tr__50: ___________td__c:out value=${file.name}//td__51: ___________td__c:out value=${file.length}//td__52: _________td__c:out value=${file.lastModified}//td__53: __________/tr__54: _________/c:forEach____Stacktrace:
If I replace the data I am looking for with dummy data, I get a print out for each item.
<td>fileName</td>
<td>fileSize</td>
<td>lastEdited</td>
gives a table with rows equal to the number of files I am looking at showing:
"fileName fileSize lastEdited"
This shows that my forEach is working, but that the references to the specific data are not.
Solution
The issue is that JSTL expects to find getter functions when searching for a property of an object. Specifically it means that there is some method with "get" as the first three characters of the method name.
In the example <td>${file.name}</td>
would work on its own since java.io.File has a method called "getName()". However, both length and lastModified do not have "get" as a prefix (.length() and .lastModified()) eventhough they simply retrieve a value.
There are many ways to solve this (extending the object, adding "get" methods, etc) but I think the simplest is using a class for a new object.
I added a class to the end of the above .java file and used that instead of java.io.File objects. It works just fine:
The New Class:
public class LogFile {
private String name;
private long length;
private String lastModified;
public LogFile(File file) {
this.name = file.getName();
this.length = file.length() / 1024;
this.lastModified = timeStampFormat.format(file.lastModified());
}
public String getName() {
return this.name;
}
public long getLength() {
return this.length;
}
public String getLastModified() {
return this.lastModified;
}
}
Using the Objects:
File[] currentLogFiles = CURRENT_LOGS_FOLDER.listFiles();
File[] capturedLogFiles = CAPTURED_LOGS_FOLDER.listFiles();
List<LogFile> currentLogs = new ArrayList<>();
List<LogFile> capturedLogs = new ArrayList<>();
if (currentLogFiles != null) {
for (File file : currentLogFiles) {
currentLogs.add(new LogFile(file));
}
}
if (capturedLogFiles != null) {
for (File file : capturedLogFiles) {
currentLogs.add(new LogFile(file));
}
}
HttpSession session = request.getSession();
session.setAttribute("currentLogs", currentLogs);
session.setAttribute("capturedLogs", capturedLogs);
as a side note: both ${file.name}
and <c:out value="${file.name}" />
work in jst as far as I have found.
Answered By - Logan Kitchen
Answer Checked By - Senaida (JavaFixing Volunteer)