Resolving Excel Export I/O Stream Error/Fatal Error Issue
Problem:
Fatal Application Error while
exporting search results data into an excel sheet.
Root Cause for the Issue is:
- The Root Cause of the issue is Because of
using the global (Instance) variables instead of using the method
variables inside the class for writing the data to the excel file.
- In Struts 1.1/1.2 the Action class behaves
like singleton. There will be only one instance of the action for all the
incoming requests. So whenever we try to build our logic using the
instance variables (The variables which we are defining at the top of the
class outside the method area) instead of local variables (or method
variables), we will face data inconsistency issues. If we are using this
kind of variables for IO operations then we will be ending with Stream
errors (like the one we faced in one of our application recently).
- In the case of our (XYZ) application,
there was a call to flushRows(100)
function on the global variable which variable was declared outside the
method (at the beginning of the respective action class). So whenever two
requests raised simultaneously, Out of two requests one request was taking
less time to complete the job, during completion of this request the I/O
stream was closed after the call to
flushRows() function or call to out.close(). So when another
request starts processing on the same, that time it finds the I/O stream
closed and it cannot proceed the request and was throwing "Stream
Closed" exception. Because it was unable to read or write to the
stream once it was closed. Since the same copy of instance variable has
been used for both the request, one user request finds the modified
variable by another request which is not correct behavior.
Solution:
To
avoid this problem there are three approaches which I proposed, out of three I
felt the last one is the best approach:
- Moving all Excel export code to different
function and using local variables to that function instead of global
(instance) variables. Implementing this functionality in a separate method
inside the action class. But this is not a good approach as action comes
under view layer and excel export needs to be implemented in the business
layer. And also here we are not separating the excel functionality to
another class which is not a proper design. But as immediate fix we can go
ahead with this approach if your implementation already in some action
class and you don’t have much time.
- Moving all excel related code to the different
class and calling these methods from the current action class. Here the
advantage is we are separating the excel related business logic from the
action layer code. One good thing about this approach is that we are
separating the Excel Export functionality code to different class, but the
bad thing about this is, we are calling this class from action class i.e.
from view layer which is not a correct design. This we can just keep as
one immediate solution for our problem.
- The Best approach is to move all the Excel
related functionalities to one utility class and call the respective
method from Model Layer (Business layer, where business logic
implemented). And avoid use of instance variables for Excel export
implementation especially for multithreaded application. If at all using
you have to take care of all the scenarios which is a very big headache.
And also proper exception handling and proper cleaning of the I/O
resources once after completing the job is very important to avoid errors.