groovyjirascriptrunner-for-jira

Why does working with XSSFWorkbook modify the original file?


I am currently working in Groovy in Jira Scriptrunner. I am using XSSFWorkbook from apache. In the script I load a "template" .xslx file, and then modify it with a script. Although the file is never saved to the server under the same filename, only workbook.write(outputStream) and workbook.close() is called, the original file on the server got modified.

Here is the code:

import org.apache.poi.xssf.usermodel.XSSFWorkbook
import releasemanagement.changes.AttachmentCreator
import other.TMPConfig
import com.atlassian.jira.issue.fields.CustomField
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.component.ComponentAccessor
import org.apache.poi.xssf.usermodel.XSSFCell

CustomFieldManager custFldManag = ComponentAccessor.getCustomFieldManager()

def issue = Issues.getByKey("ISSUEKEY")

def attachmentCreator = new AttachmentCreator()
def attachmentManager = ComponentAccessor.getAttachmentManager()

def wb = new XSSFWorkbook(new File("path/Template.xlsx"))

CustomField currCF
XSSFCell currCell
String reportType = "Initial"

TMPConfig.fieldToCell.each{k, v ->
    List<Integer> cellCoordinates = v.get(reportType)
    if(cellCoordinates){
        currCell = wb.getSheetAt(cellCoordinates[0]).getRow(cellCoordinates[1]).getCell(cellCoordinates[2])
        if(currCell){
            currCF = custFldManag.getCustomFieldObject(k)
            currCell.setCellValue(currCF.getValue(issue).toString())
        }
    }
}

ByteArrayOutputStream out = new ByteArrayOutputStream()
wb.write(out)
wb.close()
ByteArrayInputStream inputStream = new ByteArrayInputStream(out.toByteArray())
out.close()

int filename_num = 1
String filename = "Dora_${reportType}_${filename_num}.xlsx"

attachmentCreator.createAttachmentForIssue(issue, filename, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", inputStream)

Currently it got solved with a workaround by my coworker:

XSSFWorkbook wb_temp = new XSSFWorkbook(new File("path/Template.xlsx"))
    ByteArrayOutputStream out_temp = new ByteArrayOutputStream()
        wb_temp.write(out_temp)
        wb_temp.close()
    ByteArrayInputStream inputStream_temp = new ByteArrayInputStream(out_temp.toByteArray())
        out_temp.close()
    XSSFWorkbook wb = new XSSFWorkbook(inputStream_temp)

But I am interested for the reason of the behavior. I would expect that the in-memory representation would not affect the original file, when it gets attached to the issue. Thank you for any insights.


Solution

  • https://poi.apache.org/apidocs/dev/org/apache/poi/xssf/usermodel/XSSFWorkbook.html#XSSFWorkbook-java.io.File-

    Opening a XSSFWorkbook from a file has a lower memory footprint than opening from an InputStream

    it's not answering your question directly but it gives you an idea that file is actually used as a memory buffer.


    I think you can use inputstream instead of creating a copy of file

    https://poi.apache.org/apidocs/dev/org/apache/poi/xssf/usermodel/XSSFWorkbook.html#XSSFWorkbook-java.io.InputStream-

    Note: Using an InputStream requires more memory than using a File

    new File("path/Template.xlsx").withInputStream{ stream->
      def wb = new XSSFWorkbook(stream)
      ...rest of your code here...
    }