Skip to content

Commit

Permalink
Fix retry mechanism for the DocFormatter and PdfConverter (#126)
Browse files Browse the repository at this point in the history
* Fix retry mechanism for the DocFormatter and DocumentConverter
  • Loading branch information
Ilya-c authored and subbotin committed Nov 8, 2020
1 parent 9d4c50c commit 32a46d1
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 39 deletions.
1 change: 1 addition & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ configure(core) {

testCompile(group: 'org.jmockit', name: 'jmockit', version: '1.15')
testCompile(group: 'junit', name: 'junit', version: '4.5')
testCompile group: 'org.mockito', name: 'mockito-core', version: '3.2.4'
testCompile(group: 'com.h2database', name: 'h2', version: '1.4.196')
testCompile(group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3')
testCompile(group: 'commons-dbcp', name: 'commons-dbcp', version: '1.4')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,29 +86,40 @@ public DocFormatter(FormatterFactoryInput formatterFactoryInput, OfficeIntegrati
public void renderDocument() {
try {
doCreateDocument(outputStream);
} catch (ReportingInterruptedException ie) {
} catch (ReportingInterruptedException ie) {
throw ie;
} catch (Exception e) {//just try again if any exceptions occurred
log.warn(String.format("An error occurred while generating doc report [%s]. System will retry to generate report again.", reportTemplate.getDocumentName()), e);

for (int i = 0; i < officeIntegration.getCountOfRetry(); i++) {
try {
checkThreadInterrupted();
doCreateDocument(outputStream);
return;
} catch (NoFreePortsException e1) {
if (e instanceof NoFreePortsException) {
throw (NoFreePortsException) e;
}
}
}

throw wrapWithReportingException("An error occurred while generating doc report.", e);
} catch (Exception e) {
doCreateDocumentWithRetries(outputStream, e, 0);
} finally {
IOUtils.closeQuietly(outputStream);
}
}

protected void doCreateDocumentWithRetries(final OutputStream outputStream, Exception lastException, int currentAttempt) {
if (officeIntegration.getCountOfRetry() != 0 && currentAttempt < officeIntegration.getCountOfRetry()) {
log.warn("An error occurred while generating doc report {}. System will retry to generate doc report again (current attempt: {}).",
reportTemplate.getDocumentName(), currentAttempt + 1);
log.debug("Last error:", lastException);
try {
Thread.sleep(officeIntegration.getRetryIntervalMs());

checkThreadInterrupted();
doCreateDocument(outputStream);
} catch (ReportingInterruptedException ie) {
throw ie;
} catch (InterruptedException e) {
throw new ReportingInterruptedException("Doc report task interrupted");
} catch (Exception e) {
doCreateDocumentWithRetries(outputStream, e, ++currentAttempt);
}
} else {
if (lastException instanceof NoFreePortsException) {
throw (NoFreePortsException) lastException;
}
throw wrapWithReportingException("An error occurred while generating doc report. All attempts failed", lastException);
}
}

protected void doCreateDocument(final OutputStream outputStream) throws NoFreePortsException {
OfficeTask officeTask = ooResourceProvider -> {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@
public class OfficeIntegration implements OfficeIntegrationAPI {
protected volatile boolean platformDependProcessManagement = true;
protected final ExecutorService executor;
protected final BlockingQueue<OfficeConnection> connectionsQueue = new LinkedBlockingDeque<OfficeConnection>();
protected final Set<OfficeConnection> connections = new CopyOnWriteArraySet<OfficeConnection>();
protected final BlockingQueue<OfficeConnection> connectionsQueue = new LinkedBlockingDeque<>();
protected final Set<OfficeConnection> connections = new CopyOnWriteArraySet<>();

protected String openOfficePath;
protected String temporaryDirPath;
protected Integer[] openOfficePorts;
protected Integer timeoutInSeconds = 60;
protected int countOfRetry = 2;
protected Integer timeoutInSeconds = DEFAULT_TIMEOUT;
protected int countOfRetry = DEFAULT_RETRY_COUNT;
protected int retryIntervalMs = DEFAULT_RETRY_INTERVAL;
protected Boolean displayDeviceAvailable = false;

public OfficeIntegration(String openOfficePath, Integer... ports) {
Expand All @@ -61,6 +62,14 @@ public void setCountOfRetry(int countOfRetry) {
this.countOfRetry = countOfRetry;
}

public int getRetryIntervalMs() {
return retryIntervalMs;
}

public void setRetryIntervalMs(int retryIntervalMs) {
this.retryIntervalMs = retryIntervalMs;
}

public String getTemporaryDirPath() {
return temporaryDirPath;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,19 @@
package com.haulmont.yarg.formatters.impl.doc.connector;

public interface OfficeIntegrationAPI {

int DEFAULT_RETRY_COUNT = 2;
int DEFAULT_RETRY_INTERVAL = 1000;
int DEFAULT_TIMEOUT = 60;

String getTemporaryDirPath();

Integer getTimeoutInSeconds();

int getCountOfRetry();

int getRetryIntervalMs();

Boolean isDisplayDeviceAvailable();

void runTaskWithTimeout(OfficeTask officeTask, int timeoutInSeconds) throws NoFreePortsException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,37 +45,54 @@ public DocumentConverterImpl(OfficeIntegrationAPI officeIntegration) {

public void convertToPdf(FileType fileType, final byte[] documentBytes, final OutputStream outputStream) {
String convertPattern = FileType.SPREADSHEET == fileType ? XLS_TO_PDF_OUTPUT_FILE : ODT_TO_PDF_OUTPUT_FILE;
convertWithRetry(convertPattern, documentBytes, outputStream);
convert(convertPattern, documentBytes, outputStream);
}

@Override
public void convertToHtml(FileType fileType, byte[] documentBytes, OutputStream outputStream) {
String convertPattern = FileType.SPREADSHEET == fileType ? XLS_TO_HTML_OUTPUT_FILE : ODT_TO_HTML_OUTPUT_FILE;
convertWithRetry(convertPattern, documentBytes, outputStream);
convert(convertPattern, documentBytes, outputStream);
}

protected void convertWithRetry(String convertPattern, final byte[] documentBytes, final OutputStream outputStream) {
protected void convert(String convertPattern, final byte[] documentBytes, final OutputStream outputStream) {
try {
convertOnes(convertPattern, documentBytes, outputStream);
} catch (ReportingInterruptedException e) {
throw e;
} catch (Exception e) {
log.warn("An error occurred while converting. System will retry to generate report again.", e);
for (int i = 0; i < officeIntegration.getCountOfRetry(); i++) {
try {
if (Thread.interrupted()) {
throw new ReportingInterruptedException("Document conversation task interrupted");
}
convertOnes(convertPattern, documentBytes, outputStream);
return;
} catch (NoFreePortsException e1) {
if (e instanceof NoFreePortsException) {
throw (NoFreePortsException) e;
}
convertWithRetries(convertPattern, documentBytes, outputStream, e, 0);
}
}

protected void convertWithRetries(final String convertPattern,
final byte[] documentBytes,
final OutputStream outputStream,
Exception lastException,
int retriesCount) {
if (officeIntegration.getCountOfRetry() != 0 && retriesCount < officeIntegration.getCountOfRetry()) {
log.warn("An error occurred while converting to {}. System will retry to convert again (Current attempt: {}).",
convertPattern, retriesCount + 1);
log.debug("Last error:", lastException);
try {
Thread.sleep(officeIntegration.getRetryIntervalMs());

if (Thread.interrupted()) {
throw new ReportingInterruptedException("Document conversation task interrupted");
}
convertOnes(convertPattern, documentBytes, outputStream);
} catch (ReportingInterruptedException ie) {
throw ie;
} catch (InterruptedException e) {
throw new ReportingInterruptedException("Document conversation task interrupted");
} catch (Exception e) {
convertWithRetries(convertPattern, documentBytes, outputStream, e, ++retriesCount);
}
} else {
if (lastException instanceof NoFreePortsException) {
throw (NoFreePortsException) lastException;
}

throw new ReportingException("An error occurred while converting.", e);
throw new ReportingException(String.format("Unable to convert to %s. All attempts failed", convertPattern), lastException);
}
}

Expand Down
50 changes: 48 additions & 2 deletions core/modules/core/test/smoketest/DocSpecificTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@
import com.haulmont.yarg.formatters.ReportFormatter;
import com.haulmont.yarg.formatters.factory.DefaultFormatterFactory;
import com.haulmont.yarg.formatters.factory.FormatterFactoryInput;
import com.haulmont.yarg.formatters.impl.DocFormatter;
import com.haulmont.yarg.formatters.impl.doc.connector.OfficeIntegration;
import com.haulmont.yarg.formatters.impl.doc.connector.OfficeIntegrationAPI;
import com.haulmont.yarg.formatters.impl.doc.connector.OfficeTask;
import com.haulmont.yarg.formatters.impl.xls.DocumentConverterImpl;
import com.haulmont.yarg.structure.BandData;
import com.haulmont.yarg.structure.BandOrientation;
import com.haulmont.yarg.structure.ReportFieldFormat;
import com.haulmont.yarg.structure.ReportOutputType;
import com.haulmont.yarg.structure.impl.ReportFieldFormatImpl;
import com.haulmont.yarg.structure.impl.ReportTemplateImpl;
Expand All @@ -35,7 +38,15 @@
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;

public class DocSpecificTest extends AbstractFormatSpecificTest{
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.*;

/**
* @author degtyarjov
* @version $Id$
*/
public class DocSpecificTest extends AbstractFormatSpecificTest {
@Test
public void testTableOdt() throws Exception {
BandData root = new BandData("Root");
Expand Down Expand Up @@ -192,4 +203,39 @@ public void run() {
countDownLatch.await();
System.out.println();
}

@Test
public void testRetryCount() throws Exception {
BandData root = createRootBand();
FormatterFactoryInput formatterFactoryInput = new FormatterFactoryInput("doc", root,
new ReportTemplateImpl("", "./modules/core/test/smoketest/test.doc",
"./modules/core/test/smoketest/test.doc", ReportOutputType.doc), null);
OfficeIntegrationAPI officeIntegrationApiMock = createOfficeIntegrationApiMock(5);
DocFormatter docFormatter = new DocFormatter(formatterFactoryInput, officeIntegrationApiMock);

// Check that in case of exception 'runTaskWithTimeout' will execute exactly retriesCount + 1 times
try {
docFormatter.renderDocument();
} catch (Exception e) {
verify(officeIntegrationApiMock, times(6))
.runTaskWithTimeout((OfficeTask) any(), anyInt());
}

officeIntegrationApiMock = createOfficeIntegrationApiMock(4);
DocumentConverterImpl pdfConverter = new DocumentConverterImpl(officeIntegrationApiMock);
try {
pdfConverter.convertToPdf(DocumentConverterImpl.FileType.DOCUMENT, null, null);
} catch (Exception e) {
verify(officeIntegrationApiMock, times(5))
.runTaskWithTimeout((OfficeTask) any(), anyInt());
}
}

protected OfficeIntegrationAPI createOfficeIntegrationApiMock(int retryCount) {
OfficeIntegrationAPI mOfficeIntegrationApi = mock(OfficeIntegrationAPI.class);
doThrow(RuntimeException.class).when(mOfficeIntegrationApi).runTaskWithTimeout((OfficeTask) any(), anyInt());
when(mOfficeIntegrationApi.getCountOfRetry()).thenReturn(retryCount);
when(mOfficeIntegrationApi.getRetryIntervalMs()).thenReturn(300);
return mOfficeIntegrationApi;
}
}

0 comments on commit 32a46d1

Please sign in to comment.