From d8736a1dcceae7dc4a6daceb49097bfb2e724a0d Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Thu, 19 Dec 2024 11:44:33 +0100 Subject: [PATCH] Do not report exceptions on long running Excel reads This change introduces two modifications: - `ClosedByInterruptException` is wrapped in `InterruptedException` instead of `RuntimeException` - when instrumentation encounters `InterruptedException` it bails early Having `ClosedByInterruptException` wrapped in `RuntimeException` meant that it is being reported as a regular `HostException` in the engine and to the user. Instead it should be treated specially since we know that it is caused by cancelling a long-running job. Since it is a statically checked exception it has to be declared and the information has to be propagated through various lambda constructs (thanks Java!). The above change alone meant that an error is not reported for `Data.read` nodes but any values dependent on it would still report `No_Such_Method` error when the exception is reported as a value. Hence the early bail out mechanism. --- .../job/ProgramExecutionSupport.scala | 9 ++++ .../control/ThreadInterruptedException.java | 8 ++- .../enso/table/excel/ExcelConnectionPool.java | 10 ++-- .../java/org/enso/table/excel/ExcelRange.java | 5 +- .../java/org/enso/table/excel/ExcelSheet.java | 6 +-- .../table/excel/ReadOnlyExcelConnection.java | 6 ++- .../excel/xssfreader/XSSFReaderSheet.java | 13 +++-- .../excel/xssfreader/XSSFReaderWorkbook.java | 20 +++++--- .../java/org/enso/table/read/ExcelReader.java | 28 ++++++---- .../table/util/ConsumerWithException.java | 45 ++++++++++++++++ .../table/util/FunctionWithException.java | 51 +++++++++++++++++++ .../org/enso/table/write/ExcelWriter.java | 25 +++++---- 12 files changed, 182 insertions(+), 44 deletions(-) create mode 100644 std-bits/table/src/main/java/org/enso/table/util/ConsumerWithException.java create mode 100644 std-bits/table/src/main/java/org/enso/table/util/FunctionWithException.java diff --git a/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/ProgramExecutionSupport.scala b/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/ProgramExecutionSupport.scala index 283ed9c2893d..b26771ff3403 100644 --- a/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/ProgramExecutionSupport.scala +++ b/engine/runtime-instrument-common/src/main/scala/org/enso/interpreter/instrument/job/ProgramExecutionSupport.scala @@ -95,6 +95,15 @@ object ProgramExecutionSupport { val onComputedValueCallback: Consumer[ExpressionValue] = { value => if (callStack.isEmpty) { logger.log(Level.FINEST, s"ON_COMPUTED ${value.getExpressionId}") + + if (VisualizationResult.isInterruptedException(value.getValue)) { + value.getValue match { + case e: AbstractTruffleException => + // Bail out early. Any references to the value will return `No_Such_Method` exception + throw new ThreadInterruptedException(e); + case _ => + } + } sendExpressionUpdate(contextId, executionFrame.syncState, value) sendVisualizationUpdates( contextId, diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/control/ThreadInterruptedException.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/control/ThreadInterruptedException.java index 2693b6ee1411..cd004b800607 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/control/ThreadInterruptedException.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/control/ThreadInterruptedException.java @@ -1,4 +1,10 @@ package org.enso.interpreter.runtime.control; /** Thrown when guest code discovers a thread interrupt. */ -public class ThreadInterruptedException extends RuntimeException {} +public class ThreadInterruptedException extends RuntimeException { + public ThreadInterruptedException() {} + + public ThreadInterruptedException(Throwable e) { + super(e); + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/excel/ExcelConnectionPool.java b/std-bits/table/src/main/java/org/enso/table/excel/ExcelConnectionPool.java index af8a43263ac3..5221576f113a 100644 --- a/std-bits/table/src/main/java/org/enso/table/excel/ExcelConnectionPool.java +++ b/std-bits/table/src/main/java/org/enso/table/excel/ExcelConnectionPool.java @@ -22,6 +22,7 @@ import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.enso.base.cache.ReloadDetector; import org.enso.table.excel.xssfreader.XSSFReaderWorkbook; +import org.enso.table.util.FunctionWithException; public class ExcelConnectionPool { public static final ExcelConnectionPool INSTANCE = new ExcelConnectionPool(); @@ -29,7 +30,7 @@ public class ExcelConnectionPool { private ExcelConnectionPool() {} public ReadOnlyExcelConnection openReadOnlyConnection(File file, ExcelFileFormat format) - throws IOException { + throws IOException, InterruptedException { synchronized (this) { if (isCurrentlyWriting) { throw new IllegalStateException( @@ -134,7 +135,7 @@ public R writeWorkbook(File file, Function writeAction) throws */ public R lockForWriting( File file, ExcelFileFormat format, File[] accompanyingFiles, Function action) - throws IOException { + throws IOException, InterruptedException { synchronized (this) { if (isCurrentlyWriting) { throw new IllegalStateException( @@ -242,7 +243,8 @@ static class ConnectionRecord { private ExcelWorkbook workbook; private IOException initializationException = null; - T withWorkbook(Function action) throws IOException { + T withWorkbook(FunctionWithException action) + throws IOException, InterruptedException { synchronized (this) { return action.apply(accessCurrentWorkbook()); } @@ -258,7 +260,7 @@ public void close() throws IOException { } } - void reopen(boolean throwOnFailure) throws IOException { + void reopen(boolean throwOnFailure) throws IOException, InterruptedException { synchronized (this) { if (workbook != null) { throw new IllegalStateException("The workbook is already open."); diff --git a/std-bits/table/src/main/java/org/enso/table/excel/ExcelRange.java b/std-bits/table/src/main/java/org/enso/table/excel/ExcelRange.java index 552f3385fd27..4d6526829d1d 100644 --- a/std-bits/table/src/main/java/org/enso/table/excel/ExcelRange.java +++ b/std-bits/table/src/main/java/org/enso/table/excel/ExcelRange.java @@ -181,7 +181,8 @@ public static ExcelRange forRows(String sheetName, int topRow, int bottomRow) { * @param sheet ExcelSheet containing the range refers to. * @return Expanded range covering the connected table of cells. */ - public static ExcelRange expandSingleCell(ExcelRange excelRange, ExcelSheet sheet) { + public static ExcelRange expandSingleCell(ExcelRange excelRange, ExcelSheet sheet) + throws InterruptedException { ExcelRow currentRow = sheet.get(excelRange.getTopRow()); if (currentRow == null || currentRow.isEmpty(excelRange.getLeftColumn())) { return new ExcelRange( @@ -337,7 +338,7 @@ public int getRowCount() { return isWholeColumn() ? Integer.MAX_VALUE : bottomRow - topRow + 1; } - public int getLastNonEmptyRow(ExcelSheet sheet) { + public int getLastNonEmptyRow(ExcelSheet sheet) throws InterruptedException { int lastRow = Math.min(sheet.getLastRow(), isWholeColumn() ? sheet.getLastRow() : bottomRow) + 1; diff --git a/std-bits/table/src/main/java/org/enso/table/excel/ExcelSheet.java b/std-bits/table/src/main/java/org/enso/table/excel/ExcelSheet.java index 4d2dd42a2a32..4dbe433d5d62 100644 --- a/std-bits/table/src/main/java/org/enso/table/excel/ExcelSheet.java +++ b/std-bits/table/src/main/java/org/enso/table/excel/ExcelSheet.java @@ -12,10 +12,10 @@ public interface ExcelSheet { String getName(); /** Gets the initial row index within the sheet (1-based). */ - int getFirstRow(); + int getFirstRow() throws InterruptedException; /** Gets the final row index within the sheet (1-based). */ - int getLastRow(); + int getLastRow() throws InterruptedException; /** * Gets the row at the given index within the sheet (1-based) @@ -23,7 +23,7 @@ public interface ExcelSheet { * @param row the row index (1-based)/ * @return the row object or null if the row index is out of range or doesn't exist. */ - ExcelRow get(int row); + ExcelRow get(int row) throws InterruptedException; /** Gets the underlying Apache POI Sheet object - may be null. Provided for Writer use only. */ Sheet getSheet(); diff --git a/std-bits/table/src/main/java/org/enso/table/excel/ReadOnlyExcelConnection.java b/std-bits/table/src/main/java/org/enso/table/excel/ReadOnlyExcelConnection.java index 3cbac859648a..894771c96670 100644 --- a/std-bits/table/src/main/java/org/enso/table/excel/ReadOnlyExcelConnection.java +++ b/std-bits/table/src/main/java/org/enso/table/excel/ReadOnlyExcelConnection.java @@ -1,7 +1,7 @@ package org.enso.table.excel; import java.io.IOException; -import java.util.function.Function; +import org.enso.table.util.FunctionWithException; public class ReadOnlyExcelConnection implements AutoCloseable { @@ -27,7 +27,9 @@ public synchronized void close() throws IOException { record = null; } - public synchronized T withWorkbook(Function f) throws IOException { + public synchronized T withWorkbook( + FunctionWithException f) + throws IOException, InterruptedException { if (record == null) { throw new IllegalStateException("ReadOnlyExcelConnection is being used after it was closed."); } diff --git a/std-bits/table/src/main/java/org/enso/table/excel/xssfreader/XSSFReaderSheet.java b/std-bits/table/src/main/java/org/enso/table/excel/xssfreader/XSSFReaderSheet.java index cdb79cbdbd5b..2fc288ab0716 100644 --- a/std-bits/table/src/main/java/org/enso/table/excel/xssfreader/XSSFReaderSheet.java +++ b/std-bits/table/src/main/java/org/enso/table/excel/xssfreader/XSSFReaderSheet.java @@ -1,6 +1,7 @@ package org.enso.table.excel.xssfreader; import java.io.IOException; +import java.nio.channels.ClosedByInterruptException; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; @@ -33,7 +34,7 @@ public XSSFReaderSheet(int sheetIdx, String sheetName, String relId, XSSFReaderW this.parent = parent; } - private synchronized void ensureReadSheetData() { + private synchronized void ensureReadSheetData() throws InterruptedException { if (hasReadSheetData) { return; } @@ -70,6 +71,8 @@ protected void onCell(int rowNumber, short columnNumber, String ref, CellValue v try { var sheet = reader.getSheet(relId); xmlReader.parse(new InputSource(sheet)); + } catch (ClosedByInterruptException e) { + throw new InterruptedException(e.getMessage()); } catch (SAXException | InvalidFormatException | IOException e) { throw new RuntimeException(e); } @@ -94,25 +97,25 @@ public String getName() { return sheetName; } - public String getDimensions() { + public String getDimensions() throws InterruptedException { ensureReadSheetData(); return dimensions; } @Override - public int getFirstRow() { + public int getFirstRow() throws InterruptedException { ensureReadSheetData(); return firstRow; } @Override - public int getLastRow() { + public int getLastRow() throws InterruptedException { ensureReadSheetData(); return lastRow; } @Override - public ExcelRow get(int row) { + public ExcelRow get(int row) throws InterruptedException { ensureReadSheetData(); if (!rowData.containsKey(row)) { diff --git a/std-bits/table/src/main/java/org/enso/table/excel/xssfreader/XSSFReaderWorkbook.java b/std-bits/table/src/main/java/org/enso/table/excel/xssfreader/XSSFReaderWorkbook.java index 6502057ff416..46f1d441914f 100644 --- a/std-bits/table/src/main/java/org/enso/table/excel/xssfreader/XSSFReaderWorkbook.java +++ b/std-bits/table/src/main/java/org/enso/table/excel/xssfreader/XSSFReaderWorkbook.java @@ -1,6 +1,7 @@ package org.enso.table.excel.xssfreader; import java.io.IOException; +import java.nio.channels.ClosedByInterruptException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -8,7 +9,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.function.Consumer; import javax.xml.XMLConstants; import javax.xml.namespace.NamespaceContext; import javax.xml.xpath.XPathConstants; @@ -26,6 +26,7 @@ import org.apache.poi.xssf.usermodel.XSSFRelation; import org.enso.table.excel.ExcelSheet; import org.enso.table.excel.ExcelWorkbook; +import org.enso.table.util.ConsumerWithException; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -90,7 +91,7 @@ public Iterator getPrefixes(String namespaceURI) { private SharedStrings sharedStrings; private XSSFReaderFormats styles; - public XSSFReaderWorkbook(String path) throws IOException { + public XSSFReaderWorkbook(String path) throws IOException, InterruptedException { this.path = path; // Read the workbook data @@ -101,7 +102,8 @@ public String getPath() { return path; } - void withReader(Consumer action) throws IOException { + void withReader(ConsumerWithException action) + throws IOException, InterruptedException { try (var pkg = OPCPackage.open(path, PackageAccess.READ)) { var reader = new XSSFReader(pkg); action.accept(reader); @@ -115,7 +117,7 @@ private record SheetInfo(int index, int sheetId, String name, String relID, bool private record NamedRange(String name, String formula) {} - private void readWorkbookData() throws IOException { + private void readWorkbookData() throws IOException, InterruptedException { withReader( reader -> { try { @@ -124,6 +126,8 @@ private void readWorkbookData() throws IOException { read1904DateSetting(workbookDoc); readSheetInfo(workbookDoc); readNamedRanges(workbookDoc); + } catch (ClosedByInterruptException e) { + throw new InterruptedException(e.getMessage()); } catch (SAXException | IOException | InvalidFormatException @@ -171,7 +175,7 @@ private void read1904DateSetting(Document workbookDoc) throws XPathExpressionExc } } - private synchronized void ensureReadShared() { + private synchronized void ensureReadShared() throws InterruptedException { if (hasReadShared) { return; } @@ -207,6 +211,8 @@ public int getUniqueCount() { styles = new XSSFReaderFormats(stylesTable); hasReadShared = true; + } catch (ClosedByInterruptException e) { + throw new InterruptedException(e.getMessage()); } catch (InvalidFormatException | IOException e) { throw new RuntimeException(e); } @@ -258,12 +264,12 @@ public String getNameFormula(String name) { return namedRange == null ? null : namedRange.formula; } - public SharedStrings getSharedStrings() { + public SharedStrings getSharedStrings() throws InterruptedException { ensureReadShared(); return sharedStrings; } - public XSSFReaderFormats getStyles() { + public XSSFReaderFormats getStyles() throws InterruptedException { ensureReadShared(); return styles; } diff --git a/std-bits/table/src/main/java/org/enso/table/read/ExcelReader.java b/std-bits/table/src/main/java/org/enso/table/read/ExcelReader.java index 6f6b289e8998..f4587fc6cb1e 100644 --- a/std-bits/table/src/main/java/org/enso/table/read/ExcelReader.java +++ b/std-bits/table/src/main/java/org/enso/table/read/ExcelReader.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.apache.poi.ss.util.CellReference; @@ -24,6 +23,7 @@ import org.enso.table.excel.ExcelWorkbook; import org.enso.table.excel.ReadOnlyExcelConnection; import org.enso.table.problems.ProblemAggregator; +import org.enso.table.util.FunctionWithException; import org.graalvm.polyglot.Context; /** A table reader for MS Excel files. */ @@ -36,7 +36,8 @@ public class ExcelReader { * @return a String[] containing the sheet names. * @throws IOException when the action fails */ - public static String[] readSheetNames(File file, ExcelFileFormat format) throws IOException { + public static String[] readSheetNames(File file, ExcelFileFormat format) + throws IOException, InterruptedException { return withWorkbook(file, format, ExcelReader::readSheetNames); } @@ -65,7 +66,8 @@ public static String[] readSheetNames(ExcelWorkbook workbook) { * @return a String[] containing the range names. * @throws IOException when the action fails */ - public static String[] readRangeNames(File file, ExcelFileFormat format) throws IOException { + public static String[] readRangeNames(File file, ExcelFileFormat format) + throws IOException, InterruptedException { return withWorkbook(file, format, ExcelWorkbook::getRangeNames); } @@ -89,7 +91,7 @@ public static Table readSheetByName( Integer row_limit, ExcelFileFormat format, ProblemAggregator problemAggregator) - throws IOException, InvalidLocationException { + throws IOException, InvalidLocationException, InterruptedException { return withWorkbook( file, format, @@ -130,7 +132,7 @@ public static Table readSheetByIndex( Integer row_limit, ExcelFileFormat format, ProblemAggregator problemAggregator) - throws IOException, InvalidLocationException { + throws IOException, InvalidLocationException, InterruptedException { return withWorkbook( file, format, @@ -175,7 +177,7 @@ public static Table readRangeByName( Integer row_limit, ExcelFileFormat format, ProblemAggregator problemAggregator) - throws IOException, InvalidLocationException { + throws IOException, InvalidLocationException, InterruptedException { return withWorkbook( file, format, @@ -202,7 +204,7 @@ public static Table readRangeByName( int skip_rows, Integer row_limit, ProblemAggregator problemAggregator) - throws InvalidLocationException { + throws InvalidLocationException, InterruptedException { int sheetIndex = workbook.getSheetIndex(rangeNameOrAddress); if (sheetIndex != -1) { return readTable( @@ -247,7 +249,7 @@ public static Table readRange( Integer row_limit, ExcelFileFormat format, ProblemAggregator problemAggregator) - throws IOException, InvalidLocationException { + throws IOException, InvalidLocationException, InterruptedException { return withWorkbook( file, format, @@ -256,7 +258,10 @@ public static Table readRange( } private static T withWorkbook( - File file, ExcelFileFormat format, Function action) throws IOException { + File file, + ExcelFileFormat format, + FunctionWithException action) + throws IOException, InterruptedException { try (ReadOnlyExcelConnection connection = ExcelConnectionPool.INSTANCE.openReadOnlyConnection(file, format)) { return connection.withWorkbook(action); @@ -270,7 +275,7 @@ public static Table readRange( int skip_rows, Integer row_limit, ProblemAggregator problemAggregator) - throws InvalidLocationException { + throws InvalidLocationException, InterruptedException { int sheetIndex = workbook.getSheetIndex(excelRange.getSheetName()); if (sheetIndex == -1) { throw new InvalidLocationException( @@ -294,7 +299,8 @@ private static Table readTable( ExcelHeaders.HeaderBehavior headers, int skipRows, int rowCount, - ProblemAggregator problemAggregator) { + ProblemAggregator problemAggregator) + throws InterruptedException { ExcelSheet sheet = workbook.getSheetAt(sheetIndex); diff --git a/std-bits/table/src/main/java/org/enso/table/util/ConsumerWithException.java b/std-bits/table/src/main/java/org/enso/table/util/ConsumerWithException.java new file mode 100644 index 000000000000..9f9343854bbd --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/util/ConsumerWithException.java @@ -0,0 +1,45 @@ +package org.enso.table.util; + +import java.util.Objects; + +/** + * Same as {@link java.util.function.Consumer} except that a one can declare a checked exception, E. + * Represents an operation that accepts a single input argument and returns no result. Unlike most + * other functional interfaces, {@code Consumer} is expected to operate via side-effects. + * + *

This is a functional interface whose functional method is + * {@link #accept(Object)}. + * + * @param the type of the input to the operation + * @param the type of the checked exception + */ +@FunctionalInterface +public interface ConsumerWithException { + + /** + * Performs this operation on the given argument. + * + * @param t the input argument + */ + void accept(T t) throws E; + + /** + * Returns a composed {@code Consumer} that performs, in sequence, this operation followed by the + * {@code after} operation. If performing either operation throws an exception, it is relayed to + * the caller of the composed operation. If performing this operation throws an exception, the + * {@code after} operation will not be performed. + * + * @param after the operation to perform after this operation + * @return a composed {@code Consumer} that performs in sequence this operation followed by the + * {@code after} operation + * @throws NullPointerException if {@code after} is null + */ + default ConsumerWithException andThen(java.util.function.Consumer after) + throws E { + Objects.requireNonNull(after); + return (T t) -> { + accept(t); + after.accept(t); + }; + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/util/FunctionWithException.java b/std-bits/table/src/main/java/org/enso/table/util/FunctionWithException.java new file mode 100644 index 000000000000..5d41f4686cb2 --- /dev/null +++ b/std-bits/table/src/main/java/org/enso/table/util/FunctionWithException.java @@ -0,0 +1,51 @@ +package org.enso.table.util; + +import java.util.Objects; +import java.util.function.Function; + +/** + * Same as {@link Function} except that a one can declare a checked exception, E. Represents a + * function that accepts one argument and produces a result. + * + *

This is a functional interface whose functional method is + * {@link #apply(Object)}. + * + * @param the type of the input to the function + * @param the type of the result of the function + * @param the type of the checked exception + */ +@FunctionalInterface +public interface FunctionWithException { + + /** + * Applies this function to the given argument. + * + * @param t the function argument + * @return the function result + */ + R apply(T t) throws E; + + default FunctionWithException compose( + FunctionWithException before) { + Objects.requireNonNull(before); + return (V v) -> apply(before.apply(v)); + } + + /** + * Returns a composed function that first applies this function to its input, and then applies the + * {@code after} function to the result. If evaluation of either function throws an exception, it + * is relayed to the caller of the composed function. + * + * @param the type of output of the {@code after} function, and of the composed function + * @param after the function to apply after this function is applied + * @return a composed function that first applies this function and then applies the {@code after} + * function + * @throws NullPointerException if after is null + * @see #compose(Function) + */ + default FunctionWithException andThen( + FunctionWithException after) { + Objects.requireNonNull(after); + return (T t) -> after.apply(apply(t)); + } +} diff --git a/std-bits/table/src/main/java/org/enso/table/write/ExcelWriter.java b/std-bits/table/src/main/java/org/enso/table/write/ExcelWriter.java index 2ced1f7a65bd..48dbc780c444 100644 --- a/std-bits/table/src/main/java/org/enso/table/write/ExcelWriter.java +++ b/std-bits/table/src/main/java/org/enso/table/write/ExcelWriter.java @@ -53,7 +53,8 @@ public static void writeTableToSheet( ExistingDataException, IllegalStateException, ColumnNameMismatchException, - ColumnCountMismatchException { + ColumnCountMismatchException, + InterruptedException { if (sheetIndex == 0 || sheetIndex > workbook.getNumberOfSheets()) { int i = 1; while (workbook.getSheet("Sheet" + i) != null) { @@ -116,7 +117,8 @@ public static void writeTableToSheet( ExistingDataException, IllegalStateException, ColumnNameMismatchException, - ColumnCountMismatchException { + ColumnCountMismatchException, + InterruptedException { int sheetIndex = workbook.getNumberOfSheets() == 0 ? -1 : workbook.getSheetIndex(sheetName); if (sheetIndex == -1) { writeTableToSheet( @@ -169,7 +171,8 @@ public static void writeTableToRange( RangeExceededException, ExistingDataException, ColumnNameMismatchException, - ColumnCountMismatchException { + ColumnCountMismatchException, + InterruptedException { Name name = workbook.getName(rangeNameOrAddress); ExcelRange excelRange; try { @@ -194,7 +197,8 @@ public static void writeTableToRange( RangeExceededException, ExistingDataException, ColumnNameMismatchException, - ColumnCountMismatchException { + ColumnCountMismatchException, + InterruptedException { int sheetIndex = workbook.getSheetIndex(range.getSheetName()); if (sheetIndex == -1) { throw new InvalidLocationException( @@ -263,7 +267,8 @@ private static void appendRangeWithTable( throws RangeExceededException, ExistingDataException, ColumnNameMismatchException, - ColumnCountMismatchException { + ColumnCountMismatchException, + InterruptedException { Table mappedTable = switch (existingDataMode) { case APPEND_BY_INDEX -> ColumnMapper.mapColumnsByPosition( @@ -333,7 +338,7 @@ private static void updateRangeWithTable( Long rowLimit, ExcelHeaders.HeaderBehavior headers, ExcelSheet sheet) - throws RangeExceededException, ExistingDataException { + throws RangeExceededException, ExistingDataException, InterruptedException { boolean writeHeaders = headers == ExcelHeaders.HeaderBehavior.USE_FIRST_ROW_AS_HEADERS; int requiredRows = Math.min(table.rowCount(), rowLimit == null ? Integer.MAX_VALUE : rowLimit.intValue()) @@ -383,7 +388,8 @@ private static void updateRangeWithTable( * @param sheet Sheet containing the range. * @return True if range is empty and clear is False, otherwise returns False. */ - private static boolean rangeIsNotEmpty(Workbook workbook, ExcelRange range, ExcelSheet sheet) { + private static boolean rangeIsNotEmpty(Workbook workbook, ExcelRange range, ExcelSheet sheet) + throws InterruptedException { ExcelRange fullRange = range.getAbsoluteRange(workbook); for (int row = fullRange.getTopRow(); row <= fullRange.getBottomRow(); row++) { ExcelRow excelRow = sheet.get(row); @@ -401,7 +407,8 @@ private static boolean rangeIsNotEmpty(Workbook workbook, ExcelRange range, Exce * @param range The range to clear. * @param sheet Sheet containing the range. */ - private static void clearRange(Workbook workbook, ExcelRange range, ExcelSheet sheet) { + private static void clearRange(Workbook workbook, ExcelRange range, ExcelSheet sheet) + throws InterruptedException { ExcelRange fullRange = range.getAbsoluteRange(workbook); for (int row = fullRange.getTopRow(); row <= fullRange.getBottomRow(); row++) { ExcelRow excelRow = sheet.get(row); @@ -547,7 +554,7 @@ private static void writeValueToCell( * @return EXCEL_COLUMN_NAMES if the range has headers, otherwise USE_FIRST_ROW_AS_HEADERS. */ private static ExcelHeaders.HeaderBehavior shouldWriteHeaders( - ExcelSheet excelSheet, int topRow, int startCol, int endCol) { + ExcelSheet excelSheet, int topRow, int startCol, int endCol) throws InterruptedException { ExcelRow row = excelSheet.get(topRow); // If the first row is missing or empty, should write headers.