Hello,
My company is using a program that calls starts LibreOffice to convert (mostly) word documents into pdf files. These documents are delivered in zip files containing about 1000 files each. The zip is unpacked and then a program launches to convert the files. This is done by starting a java runtime which then launches LibreOffice via commandline commands. This process is given a minute of time to do its work, which is usually enough. If a timeout happens, it usually means the document has something that makes it hard to open by LO. In case of a timeout, the libreoffice process is killed.
This code has been working rather well for LibreOffice 3 and 4. Because some of the files could not be correctly converted by these version of LibreOffice, we recently upgraded to LibreOffice 5. This went okay for about a month. Suddenly, we saw that the speed of the conversion was reduced to a crawl. Converting a package of files , which took between 30 minutes and an hour suddenly started taking over 6 hours. Not only that, but the libreoffice also timed. When I investigated, I noticed that the kill command would now have 2 soffice.exe processes and 2 soffice.bin processes and that a lot more times the timeout would be triggered. I also noticed that when I killed the processes manually via taskmanager, one of them would automatically return after a short while, but with a different process id as soon as even a single file was given to the convertor program. Below is the code of the process that is launched for each document that needs converting.
The code for the converting :
package be.fgov.health.converter.libreoffice;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import com.sun.star.beans.PropertyValue;
import com.sun.star.comp.helper.Bootstrap;
import com.sun.star.comp.helper.BootstrapException;
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XStorable;
import com.sun.star.lang.DisposedException;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.uno.Exception;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import com.sun.star.util.XCloseable;
public class DocumentConverter
{
public static void main(String[] args) throws IOException
{
int exitCode = new DocumentConverter().run(args);
System.out.println("ProgramEnd");
System.exit(exitCode);
}
private int run(String[] args) throws IOException
{
System.out.println("start run");
String sourcePath = args[0];
String targetPath = args[1];
String fileName = extractFileName(targetPath);
File errorFile = new File(args[2]);
System.out.println("errorFile" + fileName);
if (!errorFile.exists())
{
errorFile.createNewFile();
}
FileWriter fileWriter = new FileWriter(errorFile, true);
BufferedWriter errorFileWriter = new BufferedWriter(fileWriter);
boolean deleteOriginal = Boolean.parseBoolean(args[3]);
String guid = args[4];
System.out.println("guid" + guid);
try
{
XComponentContext xContext = Bootstrap.bootstrap();
XMultiComponentFactory xMultiComponentFactory = xContext.getServiceManager();
XComponentLoader xComponentLoader = (XComponentLoader) UnoRuntime.queryInterface(XComponentLoader.class,
xMultiComponentFactory.createInstanceWithContext("com.sun.star.frame.Desktop", xContext));
String sourceUrl = makeUrlCorrectForLO(sourcePath);
PropertyValue propertyValues[] = new com.sun.star.beans.PropertyValue[1];
propertyValues[0] = new com.sun.star.beans.PropertyValue();
propertyValues[0].Name = "Hidden";
propertyValues[0].Value = new Boolean(true);
System.out.println("propertyValues");
XComponent oDocToStore = xComponentLoader.loadComponentFromURL(sourceUrl, "_blank", 0, propertyValues);
XStorable xStorable = (XStorable) UnoRuntime.queryInterface(XStorable.class, oDocToStore);
String targetUrl = makeUrlCorrectForLO(targetPath);
System.out.println("targetUrl" + targetUrl);
xStorable.storeToURL(targetUrl, getConversionProperties(fileName));
System.out.println("PDFCreated");
XCloseable xCloseable = (XCloseable) UnoRuntime.queryInterface(XCloseable.class, xStorable);
if (xCloseable != null)
{
xCloseable.close(false);
}
else
{
XComponent xComp = (XComponent) UnoRuntime.queryInterface(XComponent.class, xStorable);
xComp.dispose();
}
if (deleteOriginal)
{
try
{
File sourceFile = new File(new URI(sourcePath));
sourceFile.delete();
}
catch (URISyntaxException e)
{
}
}
}
catch (DisposedException e)
{
errorFileWriter.write(getErrorXml("Unable to convert " + sourcePath.replaceAll("\\\\", "/") + " to "
+ targetPath.replaceAll("\\\\", "/") + " : " + e.getMessage() + "\n" + getStackTraceAsString(e),
guid));
errorFileWriter.close();
System.out.println("DisposedException errorFile: " +getErrorXml("Unable to convert " + sourcePath.replaceAll("\\\\", "/") + " to "
+ targetPath.replaceAll("\\\\", "/") + " : " + e.getMessage() + "\n" + getStackTraceAsString(e),
guid));
return 1;
}
catch (BootstrapException e)
{
errorFileWriter.write(getErrorXml("Unable to convert " + sourcePath.replaceAll("\\\\", "/") + " to "
+ targetPath.replaceAll("\\\\", "/") + " : " + e.getMessage() + "\n" + getStackTraceAsString(e),
guid));
errorFileWriter.close();
System.out.println("BootstrapException errorFile: " +getErrorXml("Unable to convert " + sourcePath.replaceAll("\\\\", "/") + " to "
+ targetPath.replaceAll("\\\\", "/") + " : " + e.getMessage() + "\n" + getStackTraceAsString(e),
guid));
return 2;
}
catch (Exception e)
{
errorFileWriter.write(getErrorXml("Unable to convert " + sourcePath.replaceAll("\\\\", "/") + " to "
+ targetPath.replaceAll("\\\\", "/") + " : " + e.getMessage() + "\n" + getStackTraceAsString(e),
guid));
errorFileWriter.close();
System.out.println("Exception errorFile: " + getErrorXml("Unable to convert " + sourcePath.replaceAll("\\\\", "/") + " to "
+ targetPath.replaceAll("\\\\", "/") + " : " + e.getMessage() + "\n" + getStackTraceAsString(e),
guid));
return 3;
/* } catch (MalformedURLException e) {
errorFileWriter.write( "Unable to convert "
+ sourcePath.replaceAll("\\\\", "/") + " to "
+ targetPath.replaceAll("\\\\", "/") + " : "
+ e.getMessage()
+ "\n" + getStackTraceAsString(e));
errorFileWriter.close();
return 4;*/
}
return 0;
}
The code above is called for each document to be converted by the code below
public String executeConversion(String rootpath, boolean deleteOriginal) throws Exception
{
logger.log(Level.INFO, "[ToPdfConverter] : converting all files in " + rootpath);
String errorFileLocation = "ErrorFile.txt";
File errorFile = new File(errorFileLocation);
if (errorFile.exists())
{
errorFile.delete();
}
errorFile.createNewFile();
FileWriter fileWriter = new FileWriter(errorFile, true);
BufferedWriter errorFileWriter = new BufferedWriter(fileWriter);
String javaRuntime =”C:/Progam Files (x86)/Java/jdk1.7.0_25/bin/java”;
String classPath = "D:/mule/mule-standalone-3.4.0/lib/boot/log4j-1.2.14.jar;C:/Program Files (x86)/LibreOffice 5/program/classes/unoil.jar;C:/Program Files (x86)/LibreOffice 5/program/classes/ridl.jar;C:/Program Files (x86)/LibreOffice 5/program/classes/unoloader.jar;C:/Program Files (x86)/LibreOffice 5/program/classes/jurt.jar;C:/Program Files (x86)/LibreOffice 5/program/classes/java_uno.jar;C:/Program Files (x86)/LibreOffice 5/program/classes/juh.jar;C:/Program Files (x86)/LibreOffice 5/program/;D:/mule/mule-standalone-3.4.0/lib/user/LibreOfficeConverter.jar";
String main = “be.fgov.health.converter.libreoffice.DocumentConverter”;
long timeout = Long.parseLong(“60000”);
String command = javaRuntime + " -classpath " + classPath + " " + main;
File dir = new File(rootpath);
File[] children = dir.listFiles(filter);
int counter = 1;
if (children != null)
{
for (int i = 0; i < children.length; i++)
{
// Files on the root level should only be the csv file, which doesn't need to be converted.
if (children[i].isDirectory() && !"META-INF".equals(children[i].getName()))
{
String mpguid = extractGuidFromFolder(children[i].getName());
File[] childrenToConvert = children[i].listFiles(filter);
for (int j = 0; j < childrenToConvert.length; j++)
{
if (!childrenToConvert[j].isDirectory())
{
File sourceFile = childrenToConvert[j];
String sourcePath = sourceFile.getAbsolutePath();
if (sourcePath.contains(".~lock"))
{
sourceFile.delete();
}
else
{
String targetPath = generateTargetPath(sourcePath);
File targetFile = new File(targetPath);
String mimetype = "";
try
{
MagicMatch match = Magic.getMagicMatch(childrenToConvert[j], false);
mimetype = match.getMimeType();
logger.log(Level.DEBUG, "mimetype : " + mimetype);
}
catch (Exception e)
{
// couldn't match the mimetype for some reason (i.e. happens when someone creates txt
// file without content.
// Just give to the libreoffice so it can try convert this document. LL-94
}
logger
.log(Level.INFO, "Converting (" + counter++ + ") " + sourcePath + " to " + targetPath);
if (PDF_MIMETYPE.equals(mimetype))
{
logger.log(Level.INFO, "File " + sourcePath + " is already pdf.");
File renamedFile = new File(childrenToConvert[j].getAbsolutePath() + ".pdf");
childrenToConvert[j].renameTo(renamedFile);
}
else
{
try
{
Process process;
logger.log(Level.DEBUG, command + " "
+ addQuotes(sourceFile.toURI().toURL().toString()) + " "
+ addQuotes(targetFile.toURI().toURL().toString()) + " "
+ addQuotes(errorFileLocation) + " " + deleteOriginal + " " + mpguid);
process = Runtime.getRuntime().exec(
command + " " + addQuotes(sourceFile.toURI().toURL().toString()) + " "
+ addQuotes(targetFile.toURI().toURL().toString()) + " "
+ addQuotes(errorFileLocation) + " " + deleteOriginal + " " + mpguid);
ConverterProcess converterProcess = new ConverterProcess(process);
converterProcess.start();
converterProcess.join(timeout);
logger.log(Level.DEBUG, "Mule.DocumentConverter : converterProcess returns "
+ converterProcess.getExitVal());
if (converterProcess.getExitVal() == null)
{
logger.error("TimeOut after " + timeout + "milliseconds");
if (!converterProcess.isPdfCreated())
{
errorFileWriter.write(getErrorXml(
"Unable to convert " + sourcePath.replaceAll("\\\\", "/") + " to "
+ targetPath.replaceAll("\\\\", "/") + " : " + "Timeout after "
+ timeout + "milliseconds", mpguid));
errorFileWriter.flush();
}
process.destroy();
killLibreOffice();
}
}
catch (Exception e)
{
logger.log(Level.DEBUG, "Mule.DocumentConverter : converterProcess Exception "
+ e.getMessage());
errorFileWriter.write(getErrorXml(
"Unable to convert " + sourcePath.replaceAll("\\\\", "/") + " to "
+ targetPath.replaceAll("\\\\", "/") + " : " + e.getMessage() + "\n"
+ getStackTraceAsString(e), mpguid));
errorFileWriter.flush();
}
}
}
}
}
}
}
}
killLibreOffice();
errorFileWriter.flush();
errorFileWriter.close();
String errors = readFileAsString(errorFileLocation);
if (!"".equals(errors))
{
errors = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<leaflets>\r\n" + errors + "</leaflets>";
}
return errors;
}
public void killLibreOffice() throws IOException, InterruptedException
{
List<String> processIds = getProcessIds("soffice.exe");
processIds.addAll(getProcessIds("soffice.bin"));
for (String processId : processIds)
{
String command = "taskkill /pid " + processId + " /f";
logger.info("Kill Libre Office with command:" + command);
Process process = Runtime.getRuntime().exec(command);
int i = process.waitFor();
if (i == 0)
{
logger.info("Process " + processId + " is killed");
}
else
{
logger.info("Unable to kill process " + processId);
process.destroy();
}
}
}