diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/Office2PdfPageConverter.java b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/Office2PdfPageConverter.java index 1e56943d8fef717b5462edfa14dc84cb1f078e02..96233285cb8cf6c1e5383238c264c64db4d1c84e 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/Office2PdfPageConverter.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/Office2PdfPageConverter.java @@ -20,21 +20,33 @@ package org.bigbluebutton.presentation.imp; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; import java.util.HashMap; import java.util.Map; +import com.sun.org.apache.xerces.internal.impl.xs.opti.DefaultDocument; +import org.apache.commons.io.FilenameUtils; import org.bigbluebutton.presentation.UploadedPresentation; +import org.jodconverter.core.document.DefaultDocumentFormatRegistry; +import org.jodconverter.core.document.DocumentFormat; +import org.jodconverter.core.job.AbstractConverter; import org.jodconverter.local.LocalConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; -public class Office2PdfPageConverter { +public abstract class Office2PdfPageConverter { private static Logger log = LoggerFactory.getLogger(Office2PdfPageConverter.class); - public boolean convert(File presentationFile, File output, int page, UploadedPresentation pres, - final LocalConverter converter){ + public static boolean convert(File presentationFile, File output, int page, UploadedPresentation pres, + LocalConverter converter){ + + FileInputStream inputStream = null; + FileOutputStream outputStream = null; + try { Map<String, Object> logData = new HashMap<>(); logData.put("meetingId", pres.getMeetingId()); @@ -46,7 +58,15 @@ public class Office2PdfPageConverter { String logStr = gson.toJson(logData); log.info(" --analytics-- data={}", logStr); - converter.convert(presentationFile).to(output).execute(); + final DocumentFormat sourceFormat = DefaultDocumentFormatRegistry.getFormatByExtension( + FilenameUtils.getExtension(presentationFile.getName())); + + inputStream = new FileInputStream(presentationFile); + outputStream = new FileOutputStream(output); + + converter.convert(inputStream).as(sourceFormat).to(outputStream).as(DefaultDocumentFormatRegistry.PDF).execute(); + outputStream.flush(); + if (output.exists()) { return true; } else { @@ -74,6 +94,22 @@ public class Office2PdfPageConverter { String logStr = gson.toJson(logData); log.error(" --analytics-- data={}", logStr, e); return false; + } finally { + if(inputStream!=null) { + try { + inputStream.close(); + } catch(Exception e) { + + } + } + + if(outputStream!=null) { + try { + outputStream.close(); + } catch(Exception e) { + + } + } } } diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java index 264b0430ade0e91ff9d8ba80a98248a3e5bc1edb..fb24ce2b2b1b9b80a35443c1020611ac37073781 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java @@ -20,6 +20,8 @@ package org.bigbluebutton.presentation.imp; import java.io.File; +import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -27,9 +29,9 @@ import org.bigbluebutton.presentation.ConversionMessageConstants; import org.bigbluebutton.presentation.SupportedFileTypes; import org.bigbluebutton.presentation.UploadedPresentation; import org.jodconverter.core.office.OfficeException; -import org.jodconverter.core.office.OfficeManager; +import org.jodconverter.core.office.OfficeUtils; import org.jodconverter.local.LocalConverter; -import org.jodconverter.local.office.LocalOfficeManager; +import org.jodconverter.local.office.ExternalOfficeManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,20 +41,12 @@ public class OfficeToPdfConversionService { private static Logger log = LoggerFactory.getLogger(OfficeToPdfConversionService.class); private OfficeDocumentValidator2 officeDocumentValidator; - private final OfficeManager officeManager; - private final LocalConverter documentConverter; + private final ArrayList<ExternalOfficeManager> officeManagers; + private ExternalOfficeManager currentManager = null; private boolean skipOfficePrecheck = false; public OfficeToPdfConversionService() throws OfficeException { - officeManager = LocalOfficeManager - .builder() - .portNumbers(8100, 8101, 8102, 8103, 8104) - .build(); - documentConverter = LocalConverter - .builder() - .officeManager(officeManager) - .filterChain(new OfficeDocumentConversionFilter()) - .build(); + officeManagers = new ArrayList<>(); } /* @@ -121,8 +115,39 @@ public class OfficeToPdfConversionService { private boolean convertOfficeDocToPdf(UploadedPresentation pres, File pdfOutput) { - Office2PdfPageConverter converter = new Office2PdfPageConverter(); - return converter.convert(pres.getUploadedFile(), pdfOutput, 0, pres, documentConverter); + boolean success = false; + int attempts = 0; + + while(!success) { + LocalConverter documentConverter = LocalConverter + .builder() + .officeManager(currentManager) + .filterChain(new OfficeDocumentConversionFilter()) + .build(); + + success = Office2PdfPageConverter.convert(pres.getUploadedFile(), pdfOutput, 0, pres, documentConverter); + + if(!success) { + // In case of failure, try with other open Office Manager + + if(++attempts != officeManagers.size()) { + // Go to next Office Manager ( if the last retry with the first one ) + int currentManagerIndex = officeManagers.indexOf(currentManager); + + boolean isLastManager = ( currentManagerIndex == officeManagers.size()-1 ); + if(isLastManager) { + currentManager = officeManagers.get(0); + } else { + currentManager = officeManagers.get(currentManagerIndex+1); + } + } else { + // We tried to use all our office managers and it's still failing + break; + } + } + } + + return success; } private void makePdfTheUploadedFileAndSetStepAsSuccess(UploadedPresentation pres, File pdf) { @@ -139,19 +164,41 @@ public class OfficeToPdfConversionService { } public void start() { - try { - officeManager.start(); - } catch (OfficeException e) { - log.error("Could not start Office Manager", e); + for(int managerIndex = 0; managerIndex < 4; managerIndex ++) { + Integer instanceNumber = managerIndex + 1; // starts at 1 + + try { + final File workingDir = new File("/var/tmp/soffice_0" +instanceNumber); + ExternalOfficeManager officeManager = ExternalOfficeManager + .builder() + .connectTimeout(2000L) + .retryInterval(500L) + .portNumber(8200 + instanceNumber) + .connectOnStart(false) // If it's true and soffice is not available, exception is thrown here ( we don't want exception here - we want the manager alive trying to reconnect ) + .workingDir(workingDir) + .build(); + + // Workaround for jodconverter not calling makeTempDir when connectOnStart=false (issue 211) + Method method = officeManager.getClass().getSuperclass().getDeclaredMethod("makeTempDir"); + method.setAccessible(true); + method.invoke(officeManager); + // End of workaround for jodconverter not calling makeTempDir + + officeManager.start(); + officeManagers.add(officeManager); + } catch (Exception e) { + log.error("Could not start Office Manager " + instanceNumber + ". Details: " + e.getMessage()); + } + + currentManager = officeManagers.get(0); } } public void stop() { try { - officeManager.stop(); - } catch (OfficeException e) { + officeManagers.forEach(officeManager -> officeManager.stop() ); + } catch (Exception e) { log.error("Could not stop Office Manager", e); } - } }