Search code examples
javaexcelapache-poishutdown-hook

Java shutdown hook and writing to an Excel Sheet


So high level I have a program that reads data from an Excel SpreadSheet stores it in a list of hash maps, does dome operations, gets a status, and then writes the information back the Excel SpreadSheet. As of right now if an Exception of some sort causes the program to crash or the program finishes normally everything is fine.

Now I am trying to handle what if a user terminates the run by pressing Ctrl + C. I figured the best way to do this would be implementing my own shutdown hook.

private static class TerminationThread extends Thread
{       
    public void run() {
            for(UserCredential user : creds)
        {
            Accel.setRow(user.getMap(), user.getRowIndex());
        }

        try {
            Accel.writeToFile();
        } catch (IOException e)  {
            e.printStackTrace();
        }
    }
}

In my Main() I register this as a shutdown hook

TerminationThread sH = new TerminationThread();
Runtime.getRuntime().addShutdownHook(sH);

When the program enters the shutdown hook everything is fine until it gets to the portion that writes to the file then it starts throwing a bunch of exceptions and at the end i get a corrupted spreadsheet.

Here is my write code:

public void writeToFile() throws IOException
{
    try 
    {
        fileOut = new FileOutputStream(theFile);
        theWorkbook.write(fileOut);
    } 
    catch (IOException e) 
    {
        throw new IOException("Exception in writeToFile(). " + e);
    }
    finally
    {
        try
        {
            fileOut.flush();
            fileOut.close();
        }
        catch(IOException e)
        {
            throw new IOException("Exception closing FileOutputStream. " + e);
        }
    }
}

What I am assume is happening is that the object that handles my excel spreadsheet is getting wiped out before the write is completed or the list of hash maps where i store the data before writing it is getting wiped out.

I have looked at Thread Joins as a possible solution to my problem but upon looking at them I don't think they are the solution to my problem.

So I guess my question is how do i get this to work the way I would like it to work

Cheers, Meinert

EDIT: Here is the exception

Exception in thread "Thread-4" org.apache.xmlbeans.impl.values.XmlValueDisconnectedException
    at org.apache.xmlbeans.impl.values.XmlObjectBase.check_orphaned(XmlObjectBase.java:1213)
    at org.apache.xmlbeans.impl.values.XmlObjectBase.newCursor(XmlObjectBase.java:243)
    at org.apache.xmlbeans.impl.values.XmlComplexContentImpl.arraySetterHelper(XmlComplexContentImpl.java:1073)
    at org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTDefinedNamesImpl.setDefinedNameArray(Unknown Source)
    at org.apache.poi.xssf.usermodel.XSSFWorkbook.saveNamedRanges(XSSFWorkbook.java:1270)
    at org.apache.poi.xssf.usermodel.XSSFWorkbook.commit(XSSFWorkbook.java:1291)
    at org.apache.poi.POIXMLDocumentPart.onSave(POIXMLDocumentPart.java:313)
    at org.apache.poi.POIXMLDocument.write(POIXMLDocument.java:173)
    at com.cba.statuschecker.ExcelManager.writeToFile(ExcelManager.java:179)
    at com.cba.statuschecker.Main$TerminationThread.run(Main.java:495)

EDIT 2: Well I figured out my own problem. I was approaching this the wrong way. My code is correct the only problem was that in my finally block I also have a file write. So when my program would execute all the way through without interruption it would execute the write twice which was the cause of the exception. I fixed this by checking if the finally block was executed and if it was the write in the shutdown hook is skipped.

Finally:

finally 
{           
    // Print out statuses
    printStatus();

    for(UserCredential user : creds)
    {
        Accel.setRow(user.getMap(), user.getRowIndex());
    }

    try {
        Accel.writeToFile();
    } catch (IOException e)  {
        e.printStackTrace();
    }

    didFinally = true;

    LOGGER.info(Log("Fin."));
    System.exit(0);
}

Shutdown Hook

private static class TerminationThread extends Thread
{           
    public void run() {
        System.out.println("Shutdown Initiated...");
        if(!didFinally){
            for(UserCredential user : creds)
            {
                Accel.setRow(user.getMap(), user.getRowIndex());
            }

            System.out.println("Writing to file...");
            try {
                Accel.writeToFile();
            } catch (IOException e)  {
                e.printStackTrace();
            }
        }

        System.out.println("Terminated!");
    }
}

Solution

  • Well I figured out my own problem. I was approaching this the wrong way. My code is correct the only problem was that in my finally block I also have a file write. So when my program would execute all the way through without interruption it would execute the write twice which was the cause of the exception. I fixed this by checking if the finally block was executed and if it was the write in the shutdown hook is skipped.

    Finally:

    finally 
    {           
        // Print out statuses
        printStatus();
    
        for(UserCredential user : creds)
        {
            Accel.setRow(user.getMap(), user.getRowIndex());
        }
    
        try {
            Accel.writeToFile();
        } catch (IOException e)  {
            e.printStackTrace();
        }
    
        didFinally = true;
    
        LOGGER.info(Log("Fin."));
        System.exit(0);
    }
    

    Shutdown Hook

    private static class TerminationThread extends Thread
    {           
        public void run() {
            System.out.println("Shutdown Initiated...");
            if(!didFinally){
                for(UserCredential user : creds)
                {
                    Accel.setRow(user.getMap(), user.getRowIndex());
                }
    
                System.out.println("Writing to file...");
                try {
                    Accel.writeToFile();
                } catch (IOException e)  {
                    e.printStackTrace();
                }
            }
    
            System.out.println("Terminated!");
        }
    }