Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix retry mechanism for the DocFormatter and PdfConverter #126

Merged
merged 2 commits into from
Feb 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ configure(core) {
compile(group: "com.jayway.jsonpath", name: "json-path", version: "2.1.0")

testCompile(group: 'junit', name: 'junit', version: '4.5')
testCompile group: 'org.mockito', name: 'mockito-core', version: '3.2.4'
testCompile(group: 'org.hsqldb', name: 'hsqldb', version: '2.3.3')
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.sun.star.text.*;
import com.sun.star.util.XReplaceable;
import com.sun.star.util.XSearchDescriptor;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -74,7 +75,7 @@ public class DocFormatter extends AbstractFormatter {

public DocFormatter(FormatterFactoryInput formatterFactoryInput, OfficeIntegrationAPI officeIntegration) {
super(formatterFactoryInput);
Preconditions.checkNotNull("\"officeIntegration\" parameter can not be null", officeIntegration);
Preconditions.checkNotNull(officeIntegration, "\"officeIntegration\" parameter can not be null");

this.officeIntegration = officeIntegration;
supportedOutputTypes.add(ReportOutputType.doc);
Expand All @@ -84,21 +85,33 @@ public DocFormatter(FormatterFactoryInput formatterFactoryInput, OfficeIntegrati
public void renderDocument() {
try {
doCreateDocument(reportTemplate.getOutputType(), outputStream);
} 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);
} catch (Exception e) {
retryDocumentCreation(reportTemplate.getOutputType(), outputStream, e, 0);
}
}

for (int i = 0; i < officeIntegration.getCountOfRetry(); i++) {
try {
doCreateDocument(reportTemplate.getOutputType(), outputStream);
return;
} catch (NoFreePortsException e1) {
if (e instanceof NoFreePortsException) {
throw (NoFreePortsException) e;
}
}
protected void retryDocumentCreation(final ReportOutputType outputType,
final OutputStream outputStream,
Exception lastTryException,
int currentAttempt) {
if (officeIntegration.getCountOfRetry() != 0 && currentAttempt < officeIntegration.getCountOfRetry()) {
log.warn(String.format("An error occurred while generating doc report [%s]. " +
"System will retry to generate report again (Current attempt: %s).",
reportTemplate.getDocumentName(), currentAttempt + 1));

log.debug(ExceptionUtils.getStackTrace(lastTryException));
try {
Thread.sleep(officeIntegration.getRetryIntervalMs());

doCreateDocument(outputType, outputStream);
} catch (Exception e) {
retryDocumentCreation(reportTemplate.getOutputType(), outputStream, e, ++currentAttempt);
}
} else {
if (lastTryException instanceof NoFreePortsException)
throw (NoFreePortsException) lastTryException;

throw wrapWithReportingException("An error occurred while generating doc report.", e);
throw wrapWithReportingException("An error occurred while generating doc report. All attempts failed", lastTryException);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import com.haulmont.yarg.exception.OpenOfficeException;
import com.sun.star.comp.helper.BootstrapException;

import java.lang.RuntimeException;
import java.util.Set;
import java.util.concurrent.*;

Expand All @@ -31,8 +30,9 @@ public class OfficeIntegration implements OfficeIntegrationAPI {
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 @@ -58,6 +58,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 @@ -23,6 +23,7 @@
import com.haulmont.yarg.formatters.impl.doc.connector.OfficeResourceProvider;
import com.haulmont.yarg.formatters.impl.doc.connector.OfficeTask;
import com.sun.star.lang.XComponent;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -46,19 +47,33 @@ public void convertToPdf(FileType fileType, final byte[] documentBytes, final Ou
try {
doConvertToPdf(convertPattern, documentBytes, outputStream);
} catch (Exception e) {
log.warn("An error occurred while converting xls to pdf. System will retry to generate report again.", e);
for (int i = 0; i < officeIntegration.getCountOfRetry(); i++) {
try {
doConvertToPdf(convertPattern, documentBytes, outputStream);
return;
} catch (NoFreePortsException e1) {
if (e instanceof NoFreePortsException) {
throw (NoFreePortsException) e;
}
}
retryPdfConversion(convertPattern, documentBytes, outputStream, e, 0);
}
}

protected void retryPdfConversion(final String pattern,
final byte[] documentBytes,
final OutputStream outputStream,
Exception lastTryException,
int retriesCount) {
if (officeIntegration.getCountOfRetry() != 0 && retriesCount < officeIntegration.getCountOfRetry()) {
log.warn(String.format("An error occurred while converting to pdf. " +
"System will retry to convert again (Current attempt: %s).", retriesCount + 1));
log.debug(ExceptionUtils.getStackTrace(lastTryException));
try {
Thread.sleep(officeIntegration.getRetryIntervalMs());

doConvertToPdf(pattern, documentBytes, outputStream);
} catch (InterruptedException e) {
throw new ReportingException("Unable to convert to pdf. Retry interrupted", e);
} catch (Exception e) {
retryPdfConversion(pattern, documentBytes, outputStream, e, ++retriesCount);
}
} else {
if (lastTryException instanceof NoFreePortsException)
throw (NoFreePortsException) lastTryException;

throw new ReportingException("An error occurred while converting xls to pdf.", e);
throw new ReportingException("Unable to convert to pdf. All attempts failed", lastTryException);
}
}

Expand Down
45 changes: 44 additions & 1 deletion core/modules/core/test/smoketest/DocSpecificTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
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.PdfConverter;
import com.haulmont.yarg.formatters.impl.xls.PdfConverterImpl;
import com.haulmont.yarg.structure.BandData;
import com.haulmont.yarg.structure.BandOrientation;
import com.haulmont.yarg.structure.ReportFieldFormat;
Expand All @@ -20,11 +25,15 @@
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;

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{
public class DocSpecificTest extends AbstractFormatSpecificTest {
@Test
public void testTableOdt() throws Exception {
BandData root = new BandData("Root");
Expand Down Expand Up @@ -182,4 +191,38 @@ public void run() {
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);
PdfConverterImpl pdfConverter = new PdfConverterImpl(officeIntegrationApiMock);
try {
pdfConverter.convertToPdf(PdfConverter.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;
}
}