loadProperties = getLoadProperties(inputFile);
+ XComponent document = null;
+ try {
+ document = loader.loadComponentFromURL(toUrl(inputFile), "_blank", 0, toUnoProperties(loadProperties));
+ } catch (IllegalArgumentException illegalArgumentException) {
+ throw new OfficeException("could not load document: " + inputFile.getName(), illegalArgumentException);
+ } catch (ErrorCodeIOException errorCodeIOException) {
+ throw new OfficeException("could not load document: " + inputFile.getName() + "; errorCode: " + errorCodeIOException.ErrCode, errorCodeIOException);
+ } catch (IOException ioException) {
+ throw new OfficeException("could not load document: " + inputFile.getName(), ioException);
+ }
+ if (document == null) {
+ throw new OfficeException("could not load document: " + inputFile.getName());
+ }
+ return document;
+ }
+
+ /**
+ * Override to modify the document after it has been loaded and before it gets
+ * saved in the new format.
+ *
+ * Does nothing by default.
+ *
+ * @param document
+ * @throws OfficeException
+ */
+ protected void modifyDocument(XComponent document) throws OfficeException {
+ // noop
+ }
+
+ private void storeDocument(XComponent document, File outputFile) throws OfficeException {
+ Map storeProperties = getStoreProperties(outputFile, document);
+ if (storeProperties == null) {
+ throw new OfficeException("unsupported conversion");
+ }
+ try {
+ cast(XStorable.class, document).storeToURL(toUrl(outputFile), toUnoProperties(storeProperties));
+ } catch (ErrorCodeIOException errorCodeIOException) {
+ throw new OfficeException("could not store document: " + outputFile.getName() + "; errorCode: " + errorCodeIOException.ErrCode, errorCodeIOException);
+ } catch (IOException ioException) {
+ throw new OfficeException("could not store document: " + outputFile.getName(), ioException);
+ }
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/OfficeDocumentConverter.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/OfficeDocumentConverter.java
new file mode 100644
index 00000000..dff7ff6f
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/OfficeDocumentConverter.java
@@ -0,0 +1,75 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.io.FilenameUtils;
+import org.artofsolving.jodconverter.document.DefaultDocumentFormatRegistry;
+import org.artofsolving.jodconverter.document.DocumentFormat;
+import org.artofsolving.jodconverter.document.DocumentFormatRegistry;
+import org.artofsolving.jodconverter.office.OfficeException;
+import org.artofsolving.jodconverter.office.OfficeManager;
+
+import com.sun.star.document.UpdateDocMode;
+
+public class OfficeDocumentConverter {
+
+ private final OfficeManager officeManager;
+ private final DocumentFormatRegistry formatRegistry;
+
+ private Map defaultLoadProperties = createDefaultLoadProperties();
+
+ public OfficeDocumentConverter(OfficeManager officeManager) {
+ this(officeManager, new DefaultDocumentFormatRegistry());
+ }
+
+ public OfficeDocumentConverter(OfficeManager officeManager, DocumentFormatRegistry formatRegistry) {
+ this.officeManager = officeManager;
+ this.formatRegistry = formatRegistry;
+ }
+
+ private Map createDefaultLoadProperties() {
+ Map loadProperties = new HashMap();
+ loadProperties.put("Hidden", true);
+ loadProperties.put("ReadOnly", true);
+ loadProperties.put("UpdateDocMode", UpdateDocMode.QUIET_UPDATE);
+ return loadProperties;
+ }
+
+ public void setDefaultLoadProperties(Map defaultLoadProperties) {
+ this.defaultLoadProperties = defaultLoadProperties;
+ }
+
+ public DocumentFormatRegistry getFormatRegistry() {
+ return formatRegistry;
+ }
+
+ public void convert(File inputFile, File outputFile) throws OfficeException {
+ String outputExtension = FilenameUtils.getExtension(outputFile.getName());
+ DocumentFormat outputFormat = formatRegistry.getFormatByExtension(outputExtension);
+ convert(inputFile, outputFile, outputFormat);
+ }
+
+ public void convert(File inputFile, File outputFile, DocumentFormat outputFormat) throws OfficeException {
+ String inputExtension = FilenameUtils.getExtension(inputFile.getName());
+ DocumentFormat inputFormat = formatRegistry.getFormatByExtension(inputExtension);
+ StandardConversionTask conversionTask = new StandardConversionTask(inputFile, outputFile, outputFormat);
+ conversionTask.setDefaultLoadProperties(defaultLoadProperties);
+ conversionTask.setInputFormat(inputFormat);
+ officeManager.execute(conversionTask);
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/OfficeDocumentUtils.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/OfficeDocumentUtils.java
new file mode 100644
index 00000000..59375651
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/OfficeDocumentUtils.java
@@ -0,0 +1,47 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter;
+
+import static org.artofsolving.jodconverter.office.OfficeUtils.*;
+
+import org.artofsolving.jodconverter.document.DocumentFamily;
+import org.artofsolving.jodconverter.office.OfficeException;
+
+
+import com.sun.star.lang.XComponent;
+import com.sun.star.lang.XServiceInfo;
+
+class OfficeDocumentUtils {
+
+ private OfficeDocumentUtils() {
+ throw new AssertionError("utility class must not be instantiated");
+ }
+
+ public static DocumentFamily getDocumentFamily(XComponent document) throws OfficeException {
+ XServiceInfo serviceInfo = cast(XServiceInfo.class, document);
+ if (serviceInfo.supportsService("com.sun.star.text.GenericTextDocument")) {
+ // NOTE: a GenericTextDocument is either a TextDocument, a WebDocument, or a GlobalDocument
+ // but this further distinction doesn't seem to matter for conversions
+ return DocumentFamily.TEXT;
+ } else if (serviceInfo.supportsService("com.sun.star.sheet.SpreadsheetDocument")) {
+ return DocumentFamily.SPREADSHEET;
+ } else if (serviceInfo.supportsService("com.sun.star.presentation.PresentationDocument")) {
+ return DocumentFamily.PRESENTATION;
+ } else if (serviceInfo.supportsService("com.sun.star.drawing.DrawingDocument")) {
+ return DocumentFamily.DRAWING;
+ } else {
+ throw new OfficeException("document of unknown family: " + serviceInfo.getImplementationName());
+ }
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/StandardConversionTask.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/StandardConversionTask.java
new file mode 100644
index 00000000..761a339d
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/StandardConversionTask.java
@@ -0,0 +1,74 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter;
+
+import static org.artofsolving.jodconverter.office.OfficeUtils.cast;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.artofsolving.jodconverter.document.DocumentFamily;
+import org.artofsolving.jodconverter.document.DocumentFormat;
+import org.artofsolving.jodconverter.office.OfficeException;
+
+import com.sun.star.lang.XComponent;
+import com.sun.star.util.XRefreshable;
+
+public class StandardConversionTask extends AbstractConversionTask {
+
+ private final DocumentFormat outputFormat;
+
+ private Map defaultLoadProperties;
+ private DocumentFormat inputFormat;
+
+ public StandardConversionTask(File inputFile, File outputFile, DocumentFormat outputFormat) {
+ super(inputFile, outputFile);
+ this.outputFormat = outputFormat;
+ }
+
+ public void setDefaultLoadProperties(Map defaultLoadProperties) {
+ this.defaultLoadProperties = defaultLoadProperties;
+ }
+
+ public void setInputFormat(DocumentFormat inputFormat) {
+ this.inputFormat = inputFormat;
+ }
+
+ @Override
+ protected void modifyDocument(XComponent document) throws OfficeException {
+ XRefreshable refreshable = cast(XRefreshable.class, document);
+ if (refreshable != null) {
+ refreshable.refresh();
+ }
+ }
+
+ @Override
+ protected Map getLoadProperties(File inputFile) {
+ Map loadProperties = new HashMap();
+ if (defaultLoadProperties != null) {
+ loadProperties.putAll(defaultLoadProperties);
+ }
+ if (inputFormat != null && inputFormat.getLoadProperties() != null) {
+ loadProperties.putAll(inputFormat.getLoadProperties());
+ }
+ return loadProperties;
+ }
+
+ @Override
+ protected Map getStoreProperties(File outputFile, XComponent document) {
+ DocumentFamily family = OfficeDocumentUtils.getDocumentFamily(document);
+ return outputFormat.getStoreProperties(family);
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/cli/Convert.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/cli/Convert.java
new file mode 100644
index 00000000..882942eb
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/cli/Convert.java
@@ -0,0 +1,126 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.cli;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.PosixParser;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.artofsolving.jodconverter.OfficeDocumentConverter;
+import org.artofsolving.jodconverter.document.DefaultDocumentFormatRegistry;
+import org.artofsolving.jodconverter.document.DocumentFormatRegistry;
+import org.artofsolving.jodconverter.document.JsonDocumentFormatRegistry;
+import org.artofsolving.jodconverter.office.OfficeManager;
+import org.artofsolving.jodconverter.office.DefaultOfficeManagerConfiguration;
+import org.json.JSONException;
+
+/**
+ * Command line interface executable.
+ */
+public class Convert {
+
+ public static final int STATUS_OK = 0;
+ public static final int STATUS_MISSING_INPUT_FILE = 1;
+ public static final int STATUS_INVALID_ARGUMENTS = 255;
+
+ private static final Option OPTION_OUTPUT_FORMAT = new Option("o", "output-format", true, "output format (e.g. pdf)");
+ private static final Option OPTION_PORT = new Option("p", "port", true, "office socket port (optional; defaults to 2002)");
+ private static final Option OPTION_REGISTRY = new Option("r", "registry", true, "document formats registry configuration file (optional)");
+ private static final Option OPTION_TIMEOUT = new Option("t", "timeout", true, "maximum conversion time in seconds (optional; defaults to 120)");
+ private static final Option OPTION_USER_PROFILE = new Option("u", "user-profile", true, "use settings from the given user installation dir (optional)");
+ private static final Options OPTIONS = initOptions();
+
+ private static final int DEFAULT_OFFICE_PORT = 2002;
+
+ private static Options initOptions() {
+ Options options = new Options();
+ options.addOption(OPTION_OUTPUT_FORMAT);
+ options.addOption(OPTION_PORT);
+ options.addOption(OPTION_REGISTRY);
+ options.addOption(OPTION_TIMEOUT);
+ options.addOption(OPTION_USER_PROFILE);
+ return options;
+ }
+
+ public static void main(String[] arguments) throws ParseException, JSONException, IOException {
+ CommandLineParser commandLineParser = new PosixParser();
+ CommandLine commandLine = commandLineParser.parse(OPTIONS, arguments);
+
+ String outputFormat = null;
+ if (commandLine.hasOption(OPTION_OUTPUT_FORMAT.getOpt())) {
+ outputFormat = commandLine.getOptionValue(OPTION_OUTPUT_FORMAT.getOpt());
+ }
+
+ int port = DEFAULT_OFFICE_PORT;
+ if (commandLine.hasOption(OPTION_PORT.getOpt())) {
+ port = Integer.parseInt(commandLine.getOptionValue(OPTION_PORT.getOpt()));
+ }
+
+ String[] fileNames = commandLine.getArgs();
+ if ((outputFormat == null && fileNames.length != 2) || fileNames.length < 1) {
+ String syntax = "java -jar jodconverter-core.jar [options] input-file output-file\n"
+ + "or [options] -o output-format input-file [input-file...]";
+ HelpFormatter helpFormatter = new HelpFormatter();
+ helpFormatter.printHelp(syntax, OPTIONS);
+ System.exit(STATUS_INVALID_ARGUMENTS);
+ }
+
+ DocumentFormatRegistry registry;
+ if (commandLine.hasOption(OPTION_REGISTRY.getOpt())) {
+ File registryFile = new File(commandLine.getOptionValue(OPTION_REGISTRY.getOpt()));
+ registry = new JsonDocumentFormatRegistry(FileUtils.readFileToString(registryFile));
+ } else {
+ registry = new DefaultDocumentFormatRegistry();
+ }
+
+ DefaultOfficeManagerConfiguration configuration = new DefaultOfficeManagerConfiguration();
+ configuration.setPortNumber(port);
+ if (commandLine.hasOption(OPTION_TIMEOUT.getOpt())) {
+ int timeout = Integer.parseInt(commandLine.getOptionValue(OPTION_TIMEOUT.getOpt()));
+ configuration.setTaskExecutionTimeout(timeout * 1000);
+ }
+ if (commandLine.hasOption(OPTION_USER_PROFILE.getOpt())) {
+ String templateProfileDir = commandLine.getOptionValue(OPTION_USER_PROFILE.getOpt());
+ configuration.setTemplateProfileDir(new File(templateProfileDir));
+ }
+
+ OfficeManager officeManager = configuration.buildOfficeManager();
+ officeManager.start();
+ OfficeDocumentConverter converter = new OfficeDocumentConverter(officeManager, registry);
+ try {
+ if (outputFormat == null) {
+ File inputFile = new File(fileNames[0]);
+ File outputFile = new File(fileNames[1]);
+ converter.convert(inputFile, outputFile);
+ } else {
+ for (int i = 0; i < fileNames.length; i++) {
+ File inputFile = new File(fileNames[i]);
+ String outputName = FilenameUtils.getBaseName(fileNames[i]) + "." + outputFormat;
+ File outputFile = new File(FilenameUtils.getFullPath(fileNames[i]) + outputName);
+ converter.convert(inputFile, outputFile);
+ }
+ }
+ } finally {
+ officeManager.stop();
+ }
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/DefaultDocumentFormatRegistry.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/DefaultDocumentFormatRegistry.java
new file mode 100644
index 00000000..5aa75ba7
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/DefaultDocumentFormatRegistry.java
@@ -0,0 +1,157 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.document;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class DefaultDocumentFormatRegistry extends SimpleDocumentFormatRegistry {
+
+ public DefaultDocumentFormatRegistry() {
+ DocumentFormat pdf = new DocumentFormat("Portable Document Format", "pdf", "application/pdf");
+ pdf.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "writer_pdf_Export"));
+ pdf.setStoreProperties(DocumentFamily.SPREADSHEET, Collections.singletonMap("FilterName", "calc_pdf_Export"));
+ pdf.setStoreProperties(DocumentFamily.PRESENTATION, Collections.singletonMap("FilterName", "impress_pdf_Export"));
+ pdf.setStoreProperties(DocumentFamily.DRAWING, Collections.singletonMap("FilterName", "draw_pdf_Export"));
+ addFormat(pdf);
+
+ DocumentFormat swf = new DocumentFormat("Macromedia Flash", "swf", "application/x-shockwave-flash");
+ swf.setStoreProperties(DocumentFamily.PRESENTATION, Collections.singletonMap("FilterName", "impress_flash_Export"));
+ swf.setStoreProperties(DocumentFamily.DRAWING, Collections.singletonMap("FilterName", "draw_flash_Export"));
+ addFormat(swf);
+
+ // disabled because it's not always available
+ //DocumentFormat xhtml = new DocumentFormat("XHTML", "xhtml", "application/xhtml+xml");
+ //xhtml.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "XHTML Writer File"));
+ //xhtml.setStoreProperties(DocumentFamily.SPREADSHEET, Collections.singletonMap("FilterName", "XHTML Calc File"));
+ //xhtml.setStoreProperties(DocumentFamily.PRESENTATION, Collections.singletonMap("FilterName", "XHTML Impress File"));
+ //addFormat(xhtml);
+
+ DocumentFormat html = new DocumentFormat("HTML", "html", "text/html");
+ // HTML is treated as Text when supplied as input, but as an output it is also
+ // available for exporting Spreadsheet and Presentation formats
+ html.setInputFamily(DocumentFamily.TEXT);
+ html.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "HTML (StarWriter)"));
+ html.setStoreProperties(DocumentFamily.SPREADSHEET, Collections.singletonMap("FilterName", "HTML (StarCalc)"));
+ html.setStoreProperties(DocumentFamily.PRESENTATION, Collections.singletonMap("FilterName", "impress_html_Export"));
+ addFormat(html);
+
+ DocumentFormat odt = new DocumentFormat("OpenDocument Text", "odt", "application/vnd.oasis.opendocument.text");
+ odt.setInputFamily(DocumentFamily.TEXT);
+ odt.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "writer8"));
+ addFormat(odt);
+
+ DocumentFormat sxw = new DocumentFormat("OpenOffice.org 1.0 Text Document", "sxw", "application/vnd.sun.xml.writer");
+ sxw.setInputFamily(DocumentFamily.TEXT);
+ sxw.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "StarOffice XML (Writer)"));
+ addFormat(sxw);
+
+ DocumentFormat doc = new DocumentFormat("Microsoft Word", "doc", "application/msword");
+ doc.setInputFamily(DocumentFamily.TEXT);
+ doc.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "MS Word 97"));
+ addFormat(doc);
+
+ DocumentFormat docx = new DocumentFormat("Microsoft Word 2007 XML", "docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
+ docx.setInputFamily(DocumentFamily.TEXT);
+ addFormat(docx);
+
+ DocumentFormat rtf = new DocumentFormat("Rich Text Format", "rtf", "text/rtf");
+ rtf.setInputFamily(DocumentFamily.TEXT);
+ rtf.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "Rich Text Format"));
+ addFormat(rtf);
+
+ DocumentFormat wpd = new DocumentFormat("WordPerfect", "wpd", "application/wordperfect");
+ wpd.setInputFamily(DocumentFamily.TEXT);
+ addFormat(wpd);
+
+ DocumentFormat txt = new DocumentFormat("Plain Text", "txt", "text/plain");
+ txt.setInputFamily(DocumentFamily.TEXT);
+ Map txtLoadAndStoreProperties = new LinkedHashMap();
+ txtLoadAndStoreProperties.put("FilterName", "Text (encoded)");
+ txtLoadAndStoreProperties.put("FilterOptions", "utf8");
+ txt.setLoadProperties(txtLoadAndStoreProperties);
+ txt.setStoreProperties(DocumentFamily.TEXT, txtLoadAndStoreProperties);
+ addFormat(txt);
+
+ DocumentFormat wikitext = new DocumentFormat("MediaWiki wikitext", "wiki", "text/x-wiki");
+ wikitext.setStoreProperties(DocumentFamily.TEXT, Collections.singletonMap("FilterName", "MediaWiki"));
+ //addFormat(wikitext);
+
+ DocumentFormat ods = new DocumentFormat("OpenDocument Spreadsheet", "ods", "application/vnd.oasis.opendocument.spreadsheet");
+ ods.setInputFamily(DocumentFamily.SPREADSHEET);
+ ods.setStoreProperties(DocumentFamily.SPREADSHEET, Collections.singletonMap("FilterName", "calc8"));
+ addFormat(ods);
+
+ DocumentFormat sxc = new DocumentFormat("OpenOffice.org 1.0 Spreadsheet", "sxc", "application/vnd.sun.xml.calc");
+ sxc.setInputFamily(DocumentFamily.SPREADSHEET);
+ sxc.setStoreProperties(DocumentFamily.SPREADSHEET, Collections.singletonMap("FilterName", "StarOffice XML (Calc)"));
+ addFormat(sxc);
+
+ DocumentFormat xls = new DocumentFormat("Microsoft Excel", "xls", "application/vnd.ms-excel");
+ xls.setInputFamily(DocumentFamily.SPREADSHEET);
+ xls.setStoreProperties(DocumentFamily.SPREADSHEET, Collections.singletonMap("FilterName", "MS Excel 97"));
+ addFormat(xls);
+
+ DocumentFormat xlsx = new DocumentFormat("Microsoft Excel 2007 XML", "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+ xlsx.setInputFamily(DocumentFamily.SPREADSHEET);
+ addFormat(xlsx);
+
+ DocumentFormat csv = new DocumentFormat("Comma Separated Values", "csv", "text/csv");
+ csv.setInputFamily(DocumentFamily.SPREADSHEET);
+ Map csvLoadAndStoreProperties = new LinkedHashMap();
+ csvLoadAndStoreProperties.put("FilterName", "Text - txt - csv (StarCalc)");
+ csvLoadAndStoreProperties.put("FilterOptions", "44,34,0"); // Field Separator: ','; Text Delimiter: '"'
+ csv.setLoadProperties(csvLoadAndStoreProperties);
+ csv.setStoreProperties(DocumentFamily.SPREADSHEET, csvLoadAndStoreProperties);
+ addFormat(csv);
+
+ DocumentFormat tsv = new DocumentFormat("Tab Separated Values", "tsv", "text/tab-separated-values");
+ tsv.setInputFamily(DocumentFamily.SPREADSHEET);
+ Map tsvLoadAndStoreProperties = new LinkedHashMap();
+ tsvLoadAndStoreProperties.put("FilterName", "Text - txt - csv (StarCalc)");
+ tsvLoadAndStoreProperties.put("FilterOptions", "9,34,0"); // Field Separator: '\t'; Text Delimiter: '"'
+ tsv.setLoadProperties(tsvLoadAndStoreProperties);
+ tsv.setStoreProperties(DocumentFamily.SPREADSHEET, tsvLoadAndStoreProperties);
+ addFormat(tsv);
+
+ DocumentFormat odp = new DocumentFormat("OpenDocument Presentation", "odp", "application/vnd.oasis.opendocument.presentation");
+ odp.setInputFamily(DocumentFamily.PRESENTATION);
+ odp.setStoreProperties(DocumentFamily.PRESENTATION, Collections.singletonMap("FilterName", "impress8"));
+ addFormat(odp);
+
+ DocumentFormat sxi = new DocumentFormat("OpenOffice.org 1.0 Presentation", "sxi", "application/vnd.sun.xml.impress");
+ sxi.setInputFamily(DocumentFamily.PRESENTATION);
+ sxi.setStoreProperties(DocumentFamily.PRESENTATION, Collections.singletonMap("FilterName", "StarOffice XML (Impress)"));
+ addFormat(sxi);
+
+ DocumentFormat ppt = new DocumentFormat("Microsoft PowerPoint", "ppt", "application/vnd.ms-powerpoint");
+ ppt.setInputFamily(DocumentFamily.PRESENTATION);
+ ppt.setStoreProperties(DocumentFamily.PRESENTATION, Collections.singletonMap("FilterName", "MS PowerPoint 97"));
+ addFormat(ppt);
+
+ DocumentFormat pptx = new DocumentFormat("Microsoft PowerPoint 2007 XML", "pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation");
+ pptx.setInputFamily(DocumentFamily.PRESENTATION);
+ addFormat(pptx);
+
+ DocumentFormat odg = new DocumentFormat("OpenDocument Drawing", "odg", "application/vnd.oasis.opendocument.graphics");
+ odg.setInputFamily(DocumentFamily.DRAWING);
+ odg.setStoreProperties(DocumentFamily.DRAWING, Collections.singletonMap("FilterName", "draw8"));
+ addFormat(odg);
+
+ DocumentFormat svg = new DocumentFormat("Scalable Vector Graphics", "svg", "image/svg+xml");
+ svg.setStoreProperties(DocumentFamily.DRAWING, Collections.singletonMap("FilterName", "draw_svg_Export"));
+ addFormat(svg);
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/DocumentFamily.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/DocumentFamily.java
new file mode 100644
index 00000000..5af67efc
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/DocumentFamily.java
@@ -0,0 +1,19 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.document;
+
+public enum DocumentFamily {
+
+ TEXT, SPREADSHEET, PRESENTATION, DRAWING
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/DocumentFormat.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/DocumentFormat.java
new file mode 100644
index 00000000..c77f2eb2
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/DocumentFormat.java
@@ -0,0 +1,99 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.document;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class DocumentFormat {
+
+ private String name;
+ private String extension;
+ private String mediaType;
+ private DocumentFamily inputFamily;
+ private Map loadProperties;
+ private Map> storePropertiesByFamily;
+
+ public DocumentFormat() {
+ // default
+ }
+
+ public DocumentFormat(String name, String extension, String mediaType) {
+ this.name = name;
+ this.extension = extension;
+ this.mediaType = mediaType;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getExtension() {
+ return extension;
+ }
+
+ public void setExtension(String extension) {
+ this.extension = extension;
+ }
+
+ public String getMediaType() {
+ return mediaType;
+ }
+
+ public void setMediaType(String mediaType) {
+ this.mediaType = mediaType;
+ }
+
+ public DocumentFamily getInputFamily() {
+ return inputFamily;
+ }
+
+ public void setInputFamily(DocumentFamily documentFamily) {
+ this.inputFamily = documentFamily;
+ }
+
+ public Map getLoadProperties() {
+ return loadProperties;
+ }
+
+ public void setLoadProperties(Map loadProperties) {
+ this.loadProperties = loadProperties;
+ }
+
+ public Map> getStorePropertiesByFamily() {
+ return storePropertiesByFamily;
+ }
+
+ public void setStorePropertiesByFamily(Map> storePropertiesByFamily) {
+ this.storePropertiesByFamily = storePropertiesByFamily;
+ }
+
+ public void setStoreProperties(DocumentFamily family, Map storeProperties) {
+ if (storePropertiesByFamily == null) {
+ storePropertiesByFamily = new HashMap>();
+ }
+ storePropertiesByFamily.put(family, storeProperties);
+ }
+
+ public Map getStoreProperties(DocumentFamily family) {
+ if (storePropertiesByFamily == null) {
+ return null;
+ }
+ return storePropertiesByFamily.get(family);
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/DocumentFormatRegistry.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/DocumentFormatRegistry.java
new file mode 100644
index 00000000..0b3bc253
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/DocumentFormatRegistry.java
@@ -0,0 +1,25 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.document;
+
+import java.util.Set;
+
+public interface DocumentFormatRegistry {
+
+ public DocumentFormat getFormatByExtension(String extension);
+
+ public DocumentFormat getFormatByMediaType(String mediaType);
+
+ public Set getOutputFormats(DocumentFamily family);
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/JsonDocumentFormatRegistry.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/JsonDocumentFormatRegistry.java
new file mode 100644
index 00000000..1989dec9
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/JsonDocumentFormatRegistry.java
@@ -0,0 +1,73 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.document;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.io.IOUtils;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class JsonDocumentFormatRegistry extends SimpleDocumentFormatRegistry {
+
+ public JsonDocumentFormatRegistry(InputStream input) throws JSONException, IOException {
+ readJsonArray(IOUtils.toString(input));
+ }
+
+ public JsonDocumentFormatRegistry(String source) throws JSONException {
+ readJsonArray(source);
+ }
+
+ private void readJsonArray(String source) throws JSONException {
+ JSONArray array = new JSONArray(source);
+ for (int i = 0; i < array.length(); i++) {
+ JSONObject jsonFormat = array.getJSONObject(i);
+ DocumentFormat format = new DocumentFormat();
+ format.setName(jsonFormat.getString("name"));
+ format.setExtension(jsonFormat.getString("extension"));
+ format.setMediaType(jsonFormat.getString("mediaType"));
+ if (jsonFormat.has("inputFamily")) {
+ format.setInputFamily(DocumentFamily.valueOf(jsonFormat.getString("inputFamily")));
+ }
+ if (jsonFormat.has("loadProperties")) {
+ format.setLoadProperties(toJavaMap(jsonFormat.getJSONObject("loadProperties")));
+ }
+ if (jsonFormat.has("storePropertiesByFamily")) {
+ JSONObject jsonStorePropertiesByFamily = jsonFormat.getJSONObject("storePropertiesByFamily");
+ for (String key : JSONObject.getNames(jsonStorePropertiesByFamily)) {
+ Map storeProperties = toJavaMap(jsonStorePropertiesByFamily.getJSONObject(key));
+ format.setStoreProperties(DocumentFamily.valueOf(key), storeProperties);
+ }
+ }
+ addFormat(format);
+ }
+ }
+
+ private Map toJavaMap(JSONObject jsonMap) throws JSONException {
+ Map map = new HashMap();
+ for (String key : JSONObject.getNames(jsonMap)) {
+ Object value = jsonMap.get(key);
+ if (value instanceof JSONObject) {
+ map.put(key, toJavaMap((JSONObject) value));
+ } else {
+ map.put(key, value);
+ }
+ }
+ return map;
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/SimpleDocumentFormatRegistry.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/SimpleDocumentFormatRegistry.java
new file mode 100644
index 00000000..d05dd392
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/document/SimpleDocumentFormatRegistry.java
@@ -0,0 +1,65 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.document;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class SimpleDocumentFormatRegistry implements DocumentFormatRegistry {
+
+ private List documentFormats = new ArrayList();
+
+ public void addFormat(DocumentFormat documentFormat) {
+ documentFormats.add(documentFormat);
+ }
+
+ public DocumentFormat getFormatByExtension(String extension) {
+ if (extension == null) {
+ return null;
+ }
+ String lowerExtension = extension.toLowerCase();
+ //TODO keep a documentByExtension map instead
+ for (DocumentFormat format : documentFormats) {
+ if (format.getExtension().equals(lowerExtension)) {
+ return format;
+ }
+ }
+ return null;
+ }
+
+ public DocumentFormat getFormatByMediaType(String mediaType) {
+ if (mediaType == null) {
+ return null;
+ }
+ //TODO keep a documentByMediaType map instead
+ for (DocumentFormat format : documentFormats) {
+ if (format.getMediaType().equals(mediaType)) {
+ return format;
+ }
+ }
+ return null;
+ }
+
+ public Set getOutputFormats(DocumentFamily family) {
+ Set formats = new HashSet();
+ for (DocumentFormat format : documentFormats) {
+ if (format.getStoreProperties(family) != null) {
+ formats.add(format);
+ }
+ }
+ return formats;
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/DefaultOfficeManagerConfiguration.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/DefaultOfficeManagerConfiguration.java
new file mode 100644
index 00000000..38c81a7e
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/DefaultOfficeManagerConfiguration.java
@@ -0,0 +1,223 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+import java.io.File;
+
+import org.artofsolving.jodconverter.process.ProcessManager;
+import org.artofsolving.jodconverter.process.PureJavaProcessManager;
+import org.artofsolving.jodconverter.process.LinuxProcessManager;
+import org.artofsolving.jodconverter.process.SigarProcessManager;
+import org.artofsolving.jodconverter.util.PlatformUtils;
+
+public class DefaultOfficeManagerConfiguration {
+
+ public static final long DEFAULT_RETRY_TIMEOUT = 120000L;
+
+ private File officeHome = OfficeUtils.getDefaultOfficeHome();
+ private OfficeConnectionProtocol connectionProtocol = OfficeConnectionProtocol.SOCKET;
+ private int[] portNumbers = new int[] { 2002 };
+ private String[] pipeNames = new String[] { "office" };
+ private String[] runAsArgs = null;
+ private File templateProfileDir = null;
+ private File workDir = new File(System.getProperty("java.io.tmpdir"));
+ private long taskQueueTimeout = 30000L; // 30 seconds
+ private long taskExecutionTimeout = 120000L; // 2 minutes
+ private int maxTasksPerProcess = 200;
+ private long retryTimeout = DEFAULT_RETRY_TIMEOUT;
+
+ private ProcessManager processManager = null; // lazily initialised
+
+ public DefaultOfficeManagerConfiguration setOfficeHome(String officeHome) throws NullPointerException, IllegalArgumentException {
+ checkArgumentNotNull("officeHome", officeHome);
+ return setOfficeHome(new File(officeHome));
+ }
+
+ public DefaultOfficeManagerConfiguration setOfficeHome(File officeHome) throws NullPointerException, IllegalArgumentException {
+ checkArgumentNotNull("officeHome", officeHome);
+ checkArgument("officeHome", officeHome.isDirectory(), "must exist and be a directory");
+ this.officeHome = officeHome;
+ return this;
+ }
+
+ public DefaultOfficeManagerConfiguration setConnectionProtocol(OfficeConnectionProtocol connectionProtocol) throws NullPointerException {
+ checkArgumentNotNull("connectionProtocol", connectionProtocol);
+ this.connectionProtocol = connectionProtocol;
+ return this;
+ }
+
+ public DefaultOfficeManagerConfiguration setPortNumber(int portNumber) {
+ this.portNumbers = new int[] { portNumber };
+ return this;
+ }
+
+ public DefaultOfficeManagerConfiguration setPortNumbers(int... portNumbers) throws NullPointerException, IllegalArgumentException {
+ checkArgumentNotNull("portNumbers", portNumbers);
+ checkArgument("portNumbers", portNumbers.length > 0, "must not be empty");
+ this.portNumbers = portNumbers;
+ return this;
+ }
+
+ public DefaultOfficeManagerConfiguration setPipeName(String pipeName) throws NullPointerException {
+ checkArgumentNotNull("pipeName", pipeName);
+ this.pipeNames = new String[] { pipeName };
+ return this;
+ }
+
+ public DefaultOfficeManagerConfiguration setPipeNames(String... pipeNames) throws NullPointerException, IllegalArgumentException {
+ checkArgumentNotNull("pipeNames", pipeNames);
+ checkArgument("pipeNames", pipeNames.length > 0, "must not be empty");
+ this.pipeNames = pipeNames;
+ return this;
+ }
+
+ public DefaultOfficeManagerConfiguration setRunAsArgs(String... runAsArgs) {
+ this.runAsArgs = runAsArgs;
+ return this;
+ }
+
+ public DefaultOfficeManagerConfiguration setTemplateProfileDir(File templateProfileDir) throws IllegalArgumentException {
+ if (templateProfileDir != null) {
+ checkArgument("templateProfileDir", templateProfileDir.isDirectory(), "must exist and be a directory");
+ }
+ this.templateProfileDir = templateProfileDir;
+ return this;
+ }
+
+ /**
+ * Sets the directory where temporary office profiles will be created.
+ *
+ * Defaults to the system temporary directory as specified by the java.io.tmpdir
system property.
+ *
+ * @param workDir
+ * @return
+ */
+ public DefaultOfficeManagerConfiguration setWorkDir(File workDir) {
+ checkArgumentNotNull("workDir", workDir);
+ this.workDir = workDir;
+ return this;
+ }
+
+ public DefaultOfficeManagerConfiguration setTaskQueueTimeout(long taskQueueTimeout) {
+ this.taskQueueTimeout = taskQueueTimeout;
+ return this;
+ }
+
+ public DefaultOfficeManagerConfiguration setTaskExecutionTimeout(long taskExecutionTimeout) {
+ this.taskExecutionTimeout = taskExecutionTimeout;
+ return this;
+ }
+
+ public DefaultOfficeManagerConfiguration setMaxTasksPerProcess(int maxTasksPerProcess) {
+ this.maxTasksPerProcess = maxTasksPerProcess;
+ return this;
+ }
+
+ /**
+ * Provide a specific {@link ProcessManager} implementation
+ *
+ * The default is to use {@link SigarProcessManager} if sigar.jar is
+ * available in the classpath, otherwise {@link LinuxProcessManager}
+ * on Linux and {@link PureJavaProcessManager} on other platforms.
+ *
+ * @param processManager
+ * @return
+ * @throws NullPointerException
+ */
+ public DefaultOfficeManagerConfiguration setProcessManager(ProcessManager processManager) throws NullPointerException {
+ checkArgumentNotNull("processManager", processManager);
+ this.processManager = processManager;
+ return this;
+ }
+
+ /**
+ * Retry timeout set in milliseconds. Used for retrying office process calls.
+ * If not set, it defaults to 2 minutes
+ *
+ * @param retryTimeout in milliseconds
+ * @return
+ */
+ public DefaultOfficeManagerConfiguration setRetryTimeout(long retryTimeout) {
+ this.retryTimeout = retryTimeout;
+ return this;
+ }
+
+ public OfficeManager buildOfficeManager() throws IllegalStateException {
+ if (officeHome == null) {
+ throw new IllegalStateException("officeHome not set and could not be auto-detected");
+ } else if (!officeHome.isDirectory()) {
+ throw new IllegalStateException("officeHome doesn't exist or is not a directory: " + officeHome);
+ } else if (!OfficeUtils.getOfficeExecutable(officeHome).isFile()) {
+ throw new IllegalStateException("invalid officeHome: it doesn't contain soffice.bin: " + officeHome);
+ }
+ if (templateProfileDir != null && !isValidProfileDir(templateProfileDir)) {
+ throw new IllegalStateException("templateProfileDir doesn't appear to contain a user profile: " + templateProfileDir);
+ }
+ if (!workDir.isDirectory()) {
+ throw new IllegalStateException("workDir doesn't exist or is not a directory: " + workDir);
+ }
+
+ if (processManager == null) {
+ processManager = findBestProcessManager();
+ }
+
+ int numInstances = connectionProtocol == OfficeConnectionProtocol.PIPE ? pipeNames.length : portNumbers.length;
+ UnoUrl[] unoUrls = new UnoUrl[numInstances];
+ for (int i = 0; i < numInstances; i++) {
+ unoUrls[i] = (connectionProtocol == OfficeConnectionProtocol.PIPE) ? UnoUrl.pipe(pipeNames[i]) : UnoUrl.socket(portNumbers[i]);
+ }
+ return new ProcessPoolOfficeManager(officeHome, unoUrls, runAsArgs, templateProfileDir, workDir, retryTimeout, taskQueueTimeout, taskExecutionTimeout, maxTasksPerProcess, processManager);
+ }
+
+ private ProcessManager findBestProcessManager() {
+ if (isSigarAvailable()) {
+ return new SigarProcessManager();
+ } else if (PlatformUtils.isLinux()) {
+ LinuxProcessManager processManager = new LinuxProcessManager();
+ if (runAsArgs != null) {
+ processManager.setRunAsArgs(runAsArgs);
+ }
+ return processManager;
+ } else {
+ // NOTE: UnixProcessManager can't be trusted to work on Solaris
+ // because of the 80-char limit on ps output there
+ return new PureJavaProcessManager();
+ }
+ }
+
+ private boolean isSigarAvailable() {
+ try {
+ Class.forName("org.hyperic.sigar.Sigar", false, getClass().getClassLoader());
+ return true;
+ } catch (ClassNotFoundException classNotFoundException) {
+ return false;
+ }
+ }
+
+ private void checkArgumentNotNull(String argName, Object argValue) throws NullPointerException {
+ if (argValue == null) {
+ throw new NullPointerException(argName + " must not be null");
+ }
+ }
+
+ private void checkArgument(String argName, boolean condition, String message) throws IllegalArgumentException {
+ if (!condition) {
+ throw new IllegalArgumentException(argName + " " + message);
+ }
+ }
+
+ private boolean isValidProfileDir(File profileDir) {
+ return new File(profileDir, "user").isDirectory();
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ExternalOfficeManager.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ExternalOfficeManager.java
new file mode 100644
index 00000000..fa86bb33
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ExternalOfficeManager.java
@@ -0,0 +1,86 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+import java.net.ConnectException;
+
+/**
+ * {@link OfficeManager} implementation that connects to an external Office process.
+ *
+ * The external Office process needs to be started manually, e.g. from the command line with
+ *
+ *
+ * soffice -accept="socket,host=127.0.0.1,port=2002;urp;"
+ *
+ *
+ * Since this implementation does not manage the Office process, it does not support auto-restarting the process if it exits unexpectedly.
+ *
+ * It will however auto-reconnect to the external process if the latter is manually restarted.
+ *
+ * This {@link OfficeManager} implementation basically provides the same behaviour as JODConverter 2.x, including using synchronized blocks for serialising office
+ * operations.
+ */
+class ExternalOfficeManager implements OfficeManager {
+
+ private final OfficeConnection connection;
+ private final boolean connectOnStart;
+
+ /**
+ * @param unoUrl
+ * @param connectOnStart
+ * should a connection be attempted on {@link #start()}? Default is true. If false, a connection will only be attempted the first time an
+ * {@link OfficeTask} is executed.
+ */
+ public ExternalOfficeManager(UnoUrl unoUrl, boolean connectOnStart) {
+ connection = new OfficeConnection(unoUrl);
+ this.connectOnStart = connectOnStart;
+ }
+
+ public void start() throws OfficeException {
+ if (connectOnStart) {
+ synchronized (connection) {
+ connect();
+ }
+ }
+ }
+
+ public void stop() {
+ synchronized (connection) {
+ if (connection.isConnected()) {
+ connection.disconnect();
+ }
+ }
+ }
+
+ public void execute(OfficeTask task) throws OfficeException {
+ synchronized (connection) {
+ if (!connection.isConnected()) {
+ connect();
+ }
+ task.execute(connection);
+ }
+ }
+
+ private void connect() {
+ try {
+ connection.connect();
+ } catch (ConnectException connectException) {
+ throw new OfficeException("could not connect to external office process", connectException);
+ }
+ }
+
+ public boolean isRunning() {
+ return connection.isConnected();
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ExternalOfficeManagerConfiguration.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ExternalOfficeManagerConfiguration.java
new file mode 100644
index 00000000..dfa86c0d
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ExternalOfficeManagerConfiguration.java
@@ -0,0 +1,47 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+public class ExternalOfficeManagerConfiguration {
+
+ private OfficeConnectionProtocol connectionProtocol = OfficeConnectionProtocol.SOCKET;
+ private int portNumber = 2002;
+ private String pipeName = "office";
+ private boolean connectOnStart = true;
+
+ public ExternalOfficeManagerConfiguration setConnectionProtocol(OfficeConnectionProtocol connectionProtocol) {
+ this.connectionProtocol = connectionProtocol;
+ return this;
+ }
+
+ public ExternalOfficeManagerConfiguration setPortNumber(int portNumber) {
+ this.portNumber = portNumber;
+ return this;
+ }
+
+ public ExternalOfficeManagerConfiguration setPipeName(String pipeName) {
+ this.pipeName = pipeName;
+ return this;
+ }
+
+ public ExternalOfficeManagerConfiguration setConnectOnStart(boolean connectOnStart) {
+ this.connectOnStart = connectOnStart;
+ return this;
+ }
+
+ public OfficeManager buildOfficeManager() {
+ UnoUrl unoUrl = connectionProtocol == OfficeConnectionProtocol.SOCKET ? UnoUrl.socket(portNumber) : UnoUrl.pipe(pipeName);
+ return new ExternalOfficeManager(unoUrl, connectOnStart);
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ManagedOfficeProcess.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ManagedOfficeProcess.java
new file mode 100644
index 00000000..ef8314dc
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ManagedOfficeProcess.java
@@ -0,0 +1,176 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+import java.net.ConnectException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.sun.star.frame.XDesktop;
+import com.sun.star.lang.DisposedException;
+
+class ManagedOfficeProcess {
+
+ private static final Integer EXIT_CODE_NEW_INSTALLATION = Integer.valueOf(81);
+
+ private final ManagedOfficeProcessSettings settings;
+
+ private final OfficeProcess process;
+ private final OfficeConnection connection;
+
+ private ExecutorService executor = Executors.newSingleThreadExecutor(new NamedThreadFactory("OfficeProcessThread"));
+
+ private final Logger logger = Logger.getLogger(getClass().getName());
+
+ public ManagedOfficeProcess(ManagedOfficeProcessSettings settings) throws OfficeException {
+ this.settings = settings;
+ process = new OfficeProcess(settings.getOfficeHome(), settings.getUnoUrl(), settings.getRunAsArgs(), settings.getTemplateProfileDir(), settings.getWorkDir(), settings
+ .getProcessManager());
+ connection = new OfficeConnection(settings.getUnoUrl());
+ }
+
+ public OfficeConnection getConnection() {
+ return connection;
+ }
+
+ public void startAndWait() throws OfficeException {
+ Future> future = executor.submit(new Runnable() {
+ public void run() {
+ doStartProcessAndConnect();
+ }
+ });
+ try {
+ future.get();
+ } catch (Exception exception) {
+ throw new OfficeException("failed to start and connect", exception);
+ }
+ }
+
+ public void stopAndWait() throws OfficeException {
+ Future> future = executor.submit(new Runnable() {
+ public void run() {
+ doStopProcess();
+ }
+ });
+ try {
+ future.get();
+ } catch (Exception exception) {
+ throw new OfficeException("failed to start and connect", exception);
+ }
+ }
+
+ public void restartAndWait() {
+ Future> future = executor.submit(new Runnable() {
+ public void run() {
+ doStopProcess();
+ doStartProcessAndConnect();
+ }
+ });
+ try {
+ future.get();
+ } catch (Exception exception) {
+ throw new OfficeException("failed to restart", exception);
+ }
+ }
+
+ public void restartDueToTaskTimeout() {
+ executor.execute(new Runnable() {
+ public void run() {
+ doTerminateProcess();
+ // will cause unexpected disconnection and subsequent restart
+ }
+ });
+ }
+
+ public void restartDueToLostConnection() {
+ executor.execute(new Runnable() {
+ public void run() {
+ try {
+ doEnsureProcessExited();
+ doStartProcessAndConnect();
+ } catch (OfficeException officeException) {
+ logger.log(Level.SEVERE, "could not restart process", officeException);
+ }
+ }
+ });
+ }
+
+ private void doStartProcessAndConnect() throws OfficeException {
+ try {
+ process.start();
+ new Retryable() {
+ protected void attempt() throws TemporaryException, Exception {
+ try {
+ connection.connect();
+ } catch (ConnectException connectException) {
+ Integer exitCode = process.getExitCode();
+ if (exitCode == null) {
+ // process is running; retry later
+ throw new TemporaryException(connectException);
+ } else if (exitCode.equals(EXIT_CODE_NEW_INSTALLATION)) {
+ // restart and retry later
+ // see http://code.google.com/p/jodconverter/issues/detail?id=84
+ logger.log(Level.WARNING, "office process died with exit code 81; restarting it");
+ process.start(true);
+ throw new TemporaryException(connectException);
+ } else {
+ throw new OfficeException("office process died with exit code " + exitCode);
+ }
+ }
+ }
+ }.execute(settings.getRetryInterval(), settings.getRetryTimeout());
+ } catch (Exception exception) {
+ throw new OfficeException("could not establish connection", exception);
+ }
+ }
+
+ private void doStopProcess() {
+ try {
+ XDesktop desktop = OfficeUtils.cast(XDesktop.class, connection.getService(OfficeUtils.SERVICE_DESKTOP));
+ desktop.terminate();
+ } catch (DisposedException disposedException) {
+ // expected
+ } catch (Exception exception) {
+ // in case we can't get hold of the desktop
+ doTerminateProcess();
+ }
+ doEnsureProcessExited();
+ }
+
+ private void doEnsureProcessExited() throws OfficeException {
+ try {
+ int exitCode = process.getExitCode(settings.getRetryInterval(), settings.getRetryTimeout());
+ logger.info("process exited with code " + exitCode);
+ } catch (RetryTimeoutException retryTimeoutException) {
+ doTerminateProcess();
+ }
+ process.deleteProfileDir();
+ }
+
+ private void doTerminateProcess() throws OfficeException {
+ try {
+ int exitCode = process.forciblyTerminate(settings.getRetryInterval(), settings.getRetryTimeout());
+ logger.info("process forcibly terminated with code " + exitCode);
+ } catch (Exception exception) {
+ throw new OfficeException("could not terminate process", exception);
+ }
+ }
+
+ boolean isConnected() {
+ return connection.isConnected();
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ManagedOfficeProcessSettings.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ManagedOfficeProcessSettings.java
new file mode 100644
index 00000000..a6fe6bf9
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ManagedOfficeProcessSettings.java
@@ -0,0 +1,97 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+import java.io.File;
+
+import org.artofsolving.jodconverter.process.ProcessManager;
+import org.artofsolving.jodconverter.process.PureJavaProcessManager;
+
+class ManagedOfficeProcessSettings {
+
+ public static final long DEFAULT_RETRY_INTERVAL = 250L;
+
+ private final UnoUrl unoUrl;
+ private File officeHome = OfficeUtils.getDefaultOfficeHome();
+ private String[] runAsArgs;
+ private File templateProfileDir;
+ private File workDir = new File(System.getProperty("java.io.tmpdir"));
+ private ProcessManager processManager = new PureJavaProcessManager();
+ private long retryTimeout = DefaultOfficeManagerConfiguration.DEFAULT_RETRY_TIMEOUT;
+ private long retryInterval = DEFAULT_RETRY_INTERVAL;
+
+ public ManagedOfficeProcessSettings(UnoUrl unoUrl) {
+ this.unoUrl = unoUrl;
+ }
+
+ public UnoUrl getUnoUrl() {
+ return unoUrl;
+ }
+
+ public File getOfficeHome() {
+ return officeHome;
+ }
+
+ public void setOfficeHome(File officeHome) {
+ this.officeHome = officeHome;
+ }
+
+ public String[] getRunAsArgs() {
+ return runAsArgs;
+ }
+
+ public void setRunAsArgs(String[] runAsArgs) {
+ this.runAsArgs = runAsArgs;
+ }
+
+ public File getTemplateProfileDir() {
+ return templateProfileDir;
+ }
+
+ public void setTemplateProfileDir(File templateProfileDir) {
+ this.templateProfileDir = templateProfileDir;
+ }
+
+ public File getWorkDir() {
+ return workDir;
+ }
+
+ public void setWorkDir(File workDir) {
+ this.workDir = workDir;
+ }
+
+ public ProcessManager getProcessManager() {
+ return processManager;
+ }
+
+ public void setProcessManager(ProcessManager processManager) {
+ this.processManager = processManager;
+ }
+
+ public long getRetryTimeout() {
+ return retryTimeout;
+ }
+
+ public void setRetryTimeout(long retryTimeout) {
+ this.retryTimeout = retryTimeout;
+ }
+
+ public long getRetryInterval() {
+ return retryInterval;
+ }
+
+ public void setRetryInterval(long retryInterval) {
+ this.retryInterval = retryInterval;
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/NamedThreadFactory.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/NamedThreadFactory.java
new file mode 100644
index 00000000..aca5afeb
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/NamedThreadFactory.java
@@ -0,0 +1,43 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * A {@link ThreadFactory} that allows for custom thread names
+ */
+class NamedThreadFactory implements ThreadFactory {
+
+ private static final AtomicInteger threadIndex = new AtomicInteger(0);
+
+ private final String baseName;
+ private final boolean daemon;
+
+ public NamedThreadFactory(String baseName) {
+ this(baseName, true);
+ }
+
+ public NamedThreadFactory(String baseName, boolean daemon) {
+ this.baseName = baseName;
+ this.daemon = daemon;
+ }
+
+ public Thread newThread(Runnable runnable) {
+ Thread thread = new Thread(runnable, baseName + "-" + threadIndex.getAndIncrement());
+ thread.setDaemon(daemon);
+ return thread;
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeConnection.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeConnection.java
new file mode 100644
index 00000000..2d494f1b
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeConnection.java
@@ -0,0 +1,117 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+import java.net.ConnectException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Logger;
+
+import com.sun.star.beans.XPropertySet;
+import com.sun.star.bridge.XBridge;
+import com.sun.star.bridge.XBridgeFactory;
+import com.sun.star.comp.helper.Bootstrap;
+import com.sun.star.connection.NoConnectException;
+import com.sun.star.connection.XConnection;
+import com.sun.star.connection.XConnector;
+import com.sun.star.lang.EventObject;
+import com.sun.star.lang.XComponent;
+import com.sun.star.lang.XEventListener;
+import com.sun.star.lang.XMultiComponentFactory;
+import com.sun.star.uno.XComponentContext;
+
+class OfficeConnection implements OfficeContext {
+
+ private static AtomicInteger bridgeIndex = new AtomicInteger();
+
+ private final UnoUrl unoUrl;
+
+ private XComponent bridgeComponent;
+ private XMultiComponentFactory serviceManager;
+ private XComponentContext componentContext;
+
+ private final List connectionEventListeners = new ArrayList();
+
+ private volatile boolean connected = false;
+
+ private XEventListener bridgeListener = new XEventListener() {
+ public void disposing(EventObject event) {
+ if (connected) {
+ connected = false;
+ logger.info(String.format("disconnected: '%s'", unoUrl));
+ OfficeConnectionEvent connectionEvent = new OfficeConnectionEvent(OfficeConnection.this);
+ for (OfficeConnectionEventListener listener : connectionEventListeners) {
+ listener.disconnected(connectionEvent);
+ }
+ }
+ // else we tried to connect to a server that doesn't speak URP
+ }
+ };
+
+ private final Logger logger = Logger.getLogger(getClass().getName());
+
+ public OfficeConnection(UnoUrl unoUrl) {
+ this.unoUrl = unoUrl;
+ }
+
+ public void addConnectionEventListener(OfficeConnectionEventListener connectionEventListener) {
+ connectionEventListeners.add(connectionEventListener);
+ }
+
+ public void connect() throws ConnectException {
+ logger.fine(String.format("connecting with connectString '%s'", unoUrl));
+ try {
+ XComponentContext localContext = Bootstrap.createInitialComponentContext(null);
+ XMultiComponentFactory localServiceManager = localContext.getServiceManager();
+ XConnector connector = OfficeUtils.cast(XConnector.class, localServiceManager.createInstanceWithContext("com.sun.star.connection.Connector", localContext));
+ XConnection connection = connector.connect(unoUrl.getConnectString());
+ XBridgeFactory bridgeFactory = OfficeUtils.cast(XBridgeFactory.class, localServiceManager.createInstanceWithContext("com.sun.star.bridge.BridgeFactory", localContext));
+ String bridgeName = "jodconverter_" + bridgeIndex.getAndIncrement();
+ XBridge bridge = bridgeFactory.createBridge(bridgeName, "urp", connection, null);
+ bridgeComponent = OfficeUtils.cast(XComponent.class, bridge);
+ bridgeComponent.addEventListener(bridgeListener);
+ serviceManager = OfficeUtils.cast(XMultiComponentFactory.class, bridge.getInstance("StarOffice.ServiceManager"));
+ XPropertySet properties = OfficeUtils.cast(XPropertySet.class, serviceManager);
+ componentContext = OfficeUtils.cast(XComponentContext.class, properties.getPropertyValue("DefaultContext"));
+ connected = true;
+ logger.info(String.format("connected: '%s'", unoUrl));
+ OfficeConnectionEvent connectionEvent = new OfficeConnectionEvent(this);
+ for (OfficeConnectionEventListener listener : connectionEventListeners) {
+ listener.connected(connectionEvent);
+ }
+ } catch (NoConnectException connectException) {
+ throw new ConnectException(String.format("connection failed: '%s'; %s", unoUrl, connectException.getMessage()));
+ } catch (Exception exception) {
+ throw new OfficeException("connection failed: "+ unoUrl, exception);
+ }
+ }
+
+ public boolean isConnected() {
+ return connected;
+ }
+
+ public synchronized void disconnect() {
+ logger.fine(String.format("disconnecting: '%s'", unoUrl));
+ bridgeComponent.dispose();
+ }
+
+ public Object getService(String serviceName) {
+ try {
+ return serviceManager.createInstanceWithContext(serviceName, componentContext);
+ } catch (Exception exception) {
+ throw new OfficeException(String.format("failed to obtain service '%s'", serviceName), exception);
+ }
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeConnectionEvent.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeConnectionEvent.java
new file mode 100644
index 00000000..c3608df3
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeConnectionEvent.java
@@ -0,0 +1,25 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+import java.util.EventObject;
+
+class OfficeConnectionEvent extends EventObject {
+
+ private static final long serialVersionUID = 2060652797570876077L;
+
+ public OfficeConnectionEvent(OfficeConnection source) {
+ super(source);
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeConnectionEventListener.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeConnectionEventListener.java
new file mode 100644
index 00000000..19b5b320
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeConnectionEventListener.java
@@ -0,0 +1,23 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+import java.util.EventListener;
+
+interface OfficeConnectionEventListener extends EventListener {
+
+ void connected(OfficeConnectionEvent event);
+
+ void disconnected(OfficeConnectionEvent event);
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeConnectionProtocol.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeConnectionProtocol.java
new file mode 100644
index 00000000..b3265521
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeConnectionProtocol.java
@@ -0,0 +1,15 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+public enum OfficeConnectionProtocol { PIPE, SOCKET }
\ No newline at end of file
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeContext.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeContext.java
new file mode 100644
index 00000000..80b9bac6
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeContext.java
@@ -0,0 +1,19 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+public interface OfficeContext {
+
+ Object getService(String serviceName);
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeException.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeException.java
new file mode 100644
index 00000000..8311755b
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeException.java
@@ -0,0 +1,26 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+public class OfficeException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public OfficeException(String message) {
+ super(message);
+ }
+
+ public OfficeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeManager.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeManager.java
new file mode 100644
index 00000000..fb4cba09
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeManager.java
@@ -0,0 +1,30 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+/**
+ * An OfficeManager knows how to execute {@link OfficeTask}s.
+ *
+ * An OfficeManager implementation will typically manage one or more
+ * {@link OfficeConnection}s.
+ */
+public interface OfficeManager {
+
+ void execute(OfficeTask task) throws OfficeException;
+
+ void start() throws OfficeException;
+
+ void stop() throws OfficeException;
+
+ boolean isRunning();
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeProcess.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeProcess.java
new file mode 100644
index 00000000..3abf9c2c
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeProcess.java
@@ -0,0 +1,209 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+import static org.artofsolving.jodconverter.process.ProcessManager.PID_NOT_FOUND;
+import static org.artofsolving.jodconverter.process.ProcessManager.PID_UNKNOWN;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import org.apache.commons.io.FileUtils;
+import org.artofsolving.jodconverter.process.ProcessManager;
+import org.artofsolving.jodconverter.process.ProcessQuery;
+import org.artofsolving.jodconverter.util.PlatformUtils;
+
+class OfficeProcess {
+
+ private final File officeHome;
+ private final UnoUrl unoUrl;
+ private final String[] runAsArgs;
+ private final File templateProfileDir;
+ private final File instanceProfileDir;
+ private final ProcessManager processManager;
+
+ private Process process;
+ private long pid = PID_UNKNOWN;
+
+ private final Logger logger = Logger.getLogger(getClass().getName());
+
+ public OfficeProcess(File officeHome, UnoUrl unoUrl, String[] runAsArgs, File templateProfileDir, File workDir, ProcessManager processManager) {
+ this.officeHome = officeHome;
+ this.unoUrl = unoUrl;
+ this.runAsArgs = runAsArgs;
+ this.templateProfileDir = templateProfileDir;
+ this.instanceProfileDir = getInstanceProfileDir(workDir, unoUrl);
+ this.processManager = processManager;
+ }
+
+ public void start() throws IOException {
+ start(false);
+ }
+
+ public void start(boolean restart) throws IOException {
+ ProcessQuery processQuery = new ProcessQuery("soffice.bin", unoUrl.getAcceptString());
+ long existingPid = processManager.findPid(processQuery);
+ if (!(existingPid == PID_NOT_FOUND || existingPid == PID_UNKNOWN)) {
+ throw new IllegalStateException(String.format("a process with acceptString '%s' is already running; pid %d",
+ unoUrl.getAcceptString(), existingPid));
+ }
+ if (!restart) {
+ prepareInstanceProfileDir();
+ }
+ List command = new ArrayList();
+ File executable = OfficeUtils.getOfficeExecutable(officeHome);
+ if (runAsArgs != null) {
+ command.addAll(Arrays.asList(runAsArgs));
+ }
+ command.add(executable.getAbsolutePath());
+ command.add("-accept=" + unoUrl.getAcceptString() + ";urp;");
+ command.add("-env:UserInstallation=" + OfficeUtils.toUrl(instanceProfileDir));
+ command.add("-headless");
+ command.add("-nocrashreport");
+ command.add("-nodefault");
+ command.add("-nofirststartwizard");
+ command.add("-nolockcheck");
+ command.add("-nologo");
+ command.add("-norestore");
+ ProcessBuilder processBuilder = new ProcessBuilder(command);
+ if (PlatformUtils.isWindows()) {
+ addBasisAndUrePaths(processBuilder);
+ }
+ logger.info(String.format("starting process with acceptString '%s' and profileDir '%s'", unoUrl, instanceProfileDir));
+ process = processBuilder.start();
+ pid = processManager.findPid(processQuery);
+ if (pid == PID_NOT_FOUND) {
+ throw new IllegalStateException(String.format("process with acceptString '%s' started but its pid could not be found",
+ unoUrl.getAcceptString()));
+ }
+ logger.info("started process" + (pid != PID_UNKNOWN ? "; pid = " + pid : ""));
+ }
+
+ private File getInstanceProfileDir(File workDir, UnoUrl unoUrl) {
+ String dirName = ".jodconverter_" + unoUrl.getAcceptString().replace(',', '_').replace('=', '-');
+ return new File(workDir, dirName);
+ }
+
+ private void prepareInstanceProfileDir() throws OfficeException {
+ if (instanceProfileDir.exists()) {
+ logger.warning(String.format("profile dir '%s' already exists; deleting", instanceProfileDir));
+ deleteProfileDir();
+ }
+ if (templateProfileDir != null) {
+ try {
+ FileUtils.copyDirectory(templateProfileDir, instanceProfileDir);
+ } catch (IOException ioException) {
+ throw new OfficeException("failed to create profileDir", ioException);
+ }
+ }
+ }
+
+ public void deleteProfileDir() {
+ if (instanceProfileDir != null) {
+ try {
+ FileUtils.deleteDirectory(instanceProfileDir);
+ } catch (IOException ioException) {
+ File oldProfileDir = new File(instanceProfileDir.getParentFile(), instanceProfileDir.getName() + ".old." + System.currentTimeMillis());
+ if (instanceProfileDir.renameTo(oldProfileDir)) {
+ logger.warning("could not delete profileDir: " + ioException.getMessage() + "; renamed it to " + oldProfileDir);
+ } else {
+ logger.severe("could not delete profileDir: " + ioException.getMessage());
+ }
+ }
+ }
+ }
+
+ private void addBasisAndUrePaths(ProcessBuilder processBuilder) throws IOException {
+ // see http://wiki.services.openoffice.org/wiki/ODF_Toolkit/Efforts/Three-Layer_OOo
+ File basisLink = new File(officeHome, "basis-link");
+ if (!basisLink.isFile()) {
+ logger.fine("no %OFFICE_HOME%/basis-link found; assuming it's OOo 2.x and we don't need to append URE and Basic paths");
+ return;
+ }
+ String basisLinkText = FileUtils.readFileToString(basisLink).trim();
+ File basisHome = new File(officeHome, basisLinkText);
+ File basisProgram = new File(basisHome, "program");
+ File ureLink = new File(basisHome, "ure-link");
+ String ureLinkText = FileUtils.readFileToString(ureLink).trim();
+ File ureHome = new File(basisHome, ureLinkText);
+ File ureBin = new File(ureHome, "bin");
+ Map environment = processBuilder.environment();
+ // Windows environment variables are case insensitive but Java maps are not :-/
+ // so let's make sure we modify the existing key
+ String pathKey = "PATH";
+ for (String key : environment.keySet()) {
+ if ("PATH".equalsIgnoreCase(key)) {
+ pathKey = key;
+ }
+ }
+ String path = environment.get(pathKey) + ";" + ureBin.getAbsolutePath() + ";" + basisProgram.getAbsolutePath();
+ logger.fine(String.format("setting %s to \"%s\"", pathKey, path));
+ environment.put(pathKey, path);
+ }
+
+ public boolean isRunning() {
+ if (process == null) {
+ return false;
+ }
+ return getExitCode() == null;
+ }
+
+ private class ExitCodeRetryable extends Retryable {
+
+ private int exitCode;
+
+ protected void attempt() throws TemporaryException, Exception {
+ try {
+ exitCode = process.exitValue();
+ } catch (IllegalThreadStateException illegalThreadStateException) {
+ throw new TemporaryException(illegalThreadStateException);
+ }
+ }
+
+ public int getExitCode() {
+ return exitCode;
+ }
+
+ }
+
+ public Integer getExitCode() {
+ try {
+ return process.exitValue();
+ } catch (IllegalThreadStateException exception) {
+ return null;
+ }
+ }
+
+ public int getExitCode(long retryInterval, long retryTimeout) throws RetryTimeoutException {
+ try {
+ ExitCodeRetryable retryable = new ExitCodeRetryable();
+ retryable.execute(retryInterval, retryTimeout);
+ return retryable.getExitCode();
+ } catch (RetryTimeoutException retryTimeoutException) {
+ throw retryTimeoutException;
+ } catch (Exception exception) {
+ throw new OfficeException("could not get process exit code", exception);
+ }
+ }
+
+ public int forciblyTerminate(long retryInterval, long retryTimeout) throws IOException, RetryTimeoutException {
+ logger.info(String.format("trying to forcibly terminate process: '" + unoUrl + "'" + (pid != PID_UNKNOWN ? " (pid " + pid + ")" : "")));
+ processManager.kill(process, pid);
+ return getExitCode(retryInterval, retryTimeout);
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeTask.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeTask.java
new file mode 100644
index 00000000..bca8d588
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeTask.java
@@ -0,0 +1,19 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+public interface OfficeTask {
+
+ void execute(OfficeContext context) throws OfficeException;
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeUtils.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeUtils.java
new file mode 100644
index 00000000..67b4c808
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeUtils.java
@@ -0,0 +1,111 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+import java.io.File;
+import java.util.Map;
+
+import org.artofsolving.jodconverter.util.PlatformUtils;
+
+import com.sun.star.beans.PropertyValue;
+import com.sun.star.uno.UnoRuntime;
+
+public class OfficeUtils {
+
+ public static final String SERVICE_DESKTOP = "com.sun.star.frame.Desktop";
+
+ private OfficeUtils() {
+ throw new AssertionError("utility class must not be instantiated");
+ }
+
+ public static T cast(Class type, Object object) {
+ return (T) UnoRuntime.queryInterface(type, object);
+ }
+
+ public static PropertyValue property(String name, Object value) {
+ PropertyValue propertyValue = new PropertyValue();
+ propertyValue.Name = name;
+ propertyValue.Value = value;
+ return propertyValue;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static PropertyValue[] toUnoProperties(Map properties) {
+ PropertyValue[] propertyValues = new PropertyValue[properties.size()];
+ int i = 0;
+ for (Map.Entry entry : properties.entrySet()) {
+ Object value = entry.getValue();
+ if (value instanceof Map) {
+ Map subProperties = (Map) value;
+ value = toUnoProperties(subProperties);
+ }
+ propertyValues[i++] = property((String) entry.getKey(), value);
+ }
+ return propertyValues;
+ }
+
+ public static String toUrl(File file) {
+ String path = file.toURI().getRawPath();
+ String url = path.startsWith("//") ? "file:" + path : "file://" + path;
+ return url.endsWith("/") ? url.substring(0, url.length() - 1) : url;
+ }
+
+ public static File getDefaultOfficeHome() {
+ if (System.getProperty("office.home") != null) {
+ return new File(System.getProperty("office.home"));
+ }
+ if (PlatformUtils.isWindows()) {
+ // %ProgramFiles(x86)% on 64-bit machines; %ProgramFiles% on 32-bit ones
+ String programFiles = System.getenv("ProgramFiles(x86)");
+ if (programFiles == null) {
+ programFiles = System.getenv("ProgramFiles");
+ }
+ return findOfficeHome(
+ programFiles + File.separator + "OpenOffice 4",
+ programFiles + File.separator + "LibreOffice 4"
+ );
+ } else if (PlatformUtils.isMac()) {
+ return findOfficeHome(
+ "/Applications/OpenOffice.org.app/Contents",
+ "/Applications/LibreOffice.app/Contents"
+ );
+ } else {
+ // Linux or other *nix variants
+ return findOfficeHome(
+ "/opt/openoffice.org3",
+ "/opt/libreoffice",
+ "/usr/lib/openoffice",
+ "/usr/lib/libreoffice"
+ );
+ }
+ }
+
+ private static File findOfficeHome(String... knownPaths) {
+ for (String path : knownPaths) {
+ File home = new File(path);
+ if (getOfficeExecutable(home).isFile()) {
+ return home;
+ }
+ }
+ return null;
+ }
+
+ public static File getOfficeExecutable(File officeHome) {
+ if (PlatformUtils.isMac()) {
+ return new File(officeHome, "MacOS/soffice.bin");
+ } else {
+ return new File(officeHome, "program/soffice.bin");
+ }
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/PooledOfficeManager.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/PooledOfficeManager.java
new file mode 100644
index 00000000..0bee2223
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/PooledOfficeManager.java
@@ -0,0 +1,110 @@
+//
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Logger;
+
+class PooledOfficeManager implements OfficeManager {
+
+ private final PooledOfficeManagerSettings settings;
+ private final ManagedOfficeProcess managedOfficeProcess;
+ private final SuspendableThreadPoolExecutor taskExecutor;
+
+ private volatile boolean stopping = false;
+ private int taskCount;
+ private Future> currentTask;
+
+ private final Logger logger = Logger.getLogger(getClass().getName());
+
+ private OfficeConnectionEventListener connectionEventListener = new OfficeConnectionEventListener() {
+ public void connected(OfficeConnectionEvent event) {
+ taskCount = 0;
+ taskExecutor.setAvailable(true);
+ }
+ public void disconnected(OfficeConnectionEvent event) {
+ taskExecutor.setAvailable(false);
+ if (stopping) {
+ // expected
+ stopping = false;
+ } else {
+ logger.warning("connection lost unexpectedly; attempting restart");
+ if (currentTask != null) {
+ currentTask.cancel(true);
+ }
+ managedOfficeProcess.restartDueToLostConnection();
+ }
+ }
+ };
+
+ public PooledOfficeManager(UnoUrl unoUrl) {
+ this(new PooledOfficeManagerSettings(unoUrl));
+ }
+
+ public PooledOfficeManager(PooledOfficeManagerSettings settings) {
+ this.settings = settings;
+ managedOfficeProcess = new ManagedOfficeProcess(settings);
+ managedOfficeProcess.getConnection().addConnectionEventListener(connectionEventListener);
+ taskExecutor = new SuspendableThreadPoolExecutor(new NamedThreadFactory("OfficeTaskThread"));
+ }
+
+ public void execute(final OfficeTask task) throws OfficeException {
+ Future> futureTask = taskExecutor.submit(new Runnable() {
+ public void run() {
+ if (settings.getMaxTasksPerProcess() > 0 && ++taskCount == settings.getMaxTasksPerProcess() + 1) {
+ logger.info(String.format("reached limit of %d maxTasksPerProcess: restarting", settings.getMaxTasksPerProcess()));
+ taskExecutor.setAvailable(false);
+ stopping = true;
+ managedOfficeProcess.restartAndWait();
+ //FIXME taskCount will be 0 rather than 1 at this point
+ }
+ task.execute(managedOfficeProcess.getConnection());
+ }
+ });
+ currentTask = futureTask;
+ try {
+ futureTask.get(settings.getTaskExecutionTimeout(), TimeUnit.MILLISECONDS);
+ } catch (TimeoutException timeoutException) {
+ managedOfficeProcess.restartDueToTaskTimeout();
+ throw new OfficeException("task did not complete within timeout", timeoutException);
+ } catch (ExecutionException executionException) {
+ if (executionException.getCause() instanceof OfficeException) {
+ throw (OfficeException) executionException.getCause();
+ } else {
+ throw new OfficeException("task failed", executionException.getCause());
+ }
+ } catch (Exception exception) {
+ throw new OfficeException("task failed", exception);
+ }
+ }
+
+ public void start() throws OfficeException {
+ managedOfficeProcess.startAndWait();
+ }
+
+ public void stop() throws OfficeException {
+ taskExecutor.setAvailable(false);
+ stopping = true;
+ taskExecutor.shutdownNow();
+ managedOfficeProcess.stopAndWait();
+ }
+
+ public boolean isRunning() {
+ return managedOfficeProcess.isConnected();
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/PooledOfficeManagerSettings.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/PooledOfficeManagerSettings.java
new file mode 100644
index 00000000..8321f89d
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/PooledOfficeManagerSettings.java
@@ -0,0 +1,43 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+class PooledOfficeManagerSettings extends ManagedOfficeProcessSettings {
+
+ public static final long DEFAULT_TASK_EXECUTION_TIMEOUT = 120000L;
+ public static final int DEFAULT_MAX_TASKS_PER_PROCESS = 200;
+
+ private long taskExecutionTimeout = DEFAULT_TASK_EXECUTION_TIMEOUT;
+ private int maxTasksPerProcess = DEFAULT_MAX_TASKS_PER_PROCESS;
+
+ public PooledOfficeManagerSettings(UnoUrl unoUrl) {
+ super(unoUrl);
+ }
+
+ public long getTaskExecutionTimeout() {
+ return taskExecutionTimeout;
+ }
+
+ public void setTaskExecutionTimeout(long taskExecutionTimeout) {
+ this.taskExecutionTimeout = taskExecutionTimeout;
+ }
+
+ public int getMaxTasksPerProcess() {
+ return maxTasksPerProcess;
+ }
+
+ public void setMaxTasksPerProcess(int maxTasksPerProcess) {
+ this.maxTasksPerProcess = maxTasksPerProcess;
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ProcessPoolOfficeManager.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ProcessPoolOfficeManager.java
new file mode 100644
index 00000000..93e3db9f
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/ProcessPoolOfficeManager.java
@@ -0,0 +1,110 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+import java.io.File;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+import org.artofsolving.jodconverter.process.ProcessManager;
+
+class ProcessPoolOfficeManager implements OfficeManager {
+
+ private final BlockingQueue pool;
+ private final PooledOfficeManager[] pooledManagers;
+ private final long taskQueueTimeout;
+
+ private volatile boolean running = false;
+
+ private final Logger logger = Logger.getLogger(ProcessPoolOfficeManager.class.getName());
+
+ public ProcessPoolOfficeManager(File officeHome, UnoUrl[] unoUrls, String[] runAsArgs, File templateProfileDir, File workDir,
+ long retryTimeout, long taskQueueTimeout, long taskExecutionTimeout, int maxTasksPerProcess,
+ ProcessManager processManager) {
+ this.taskQueueTimeout = taskQueueTimeout;
+ pool = new ArrayBlockingQueue(unoUrls.length);
+ pooledManagers = new PooledOfficeManager[unoUrls.length];
+ for (int i = 0; i < unoUrls.length; i++) {
+ PooledOfficeManagerSettings settings = new PooledOfficeManagerSettings(unoUrls[i]);
+ settings.setRunAsArgs(runAsArgs);
+ settings.setTemplateProfileDir(templateProfileDir);
+ settings.setWorkDir(workDir);
+ settings.setOfficeHome(officeHome);
+ settings.setRetryTimeout(retryTimeout);
+ settings.setTaskExecutionTimeout(taskExecutionTimeout);
+ settings.setMaxTasksPerProcess(maxTasksPerProcess);
+ settings.setProcessManager(processManager);
+ pooledManagers[i] = new PooledOfficeManager(settings);
+ }
+ logger.info("ProcessManager implementation is " + processManager.getClass().getSimpleName());
+ }
+
+ public synchronized void start() throws OfficeException {
+ for (int i = 0; i < pooledManagers.length; i++) {
+ pooledManagers[i].start();
+ releaseManager(pooledManagers[i]);
+ }
+ running = true;
+ }
+
+ public void execute(OfficeTask task) throws IllegalStateException, OfficeException {
+ if (!running) {
+ throw new IllegalStateException("this OfficeManager is currently stopped");
+ }
+ PooledOfficeManager manager = null;
+ try {
+ manager = acquireManager();
+ if (manager == null) {
+ throw new OfficeException("no office manager available");
+ }
+ manager.execute(task);
+ } finally {
+ if (manager != null) {
+ releaseManager(manager);
+ }
+ }
+ }
+
+ public synchronized void stop() throws OfficeException {
+ running = false;
+ logger.info("stopping");
+ pool.clear();
+ for (int i = 0; i < pooledManagers.length; i++) {
+ pooledManagers[i].stop();
+ }
+ logger.info("stopped");
+ }
+
+ private PooledOfficeManager acquireManager() {
+ try {
+ return pool.poll(taskQueueTimeout, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException interruptedException) {
+ throw new OfficeException("interrupted", interruptedException);
+ }
+ }
+
+ private void releaseManager(PooledOfficeManager manager) {
+ try {
+ pool.put(manager);
+ } catch (InterruptedException interruptedException) {
+ throw new OfficeException("interrupted", interruptedException);
+ }
+ }
+
+ public boolean isRunning() {
+ return running;
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/RetryTimeoutException.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/RetryTimeoutException.java
new file mode 100644
index 00000000..7144926b
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/RetryTimeoutException.java
@@ -0,0 +1,23 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+class RetryTimeoutException extends Exception {
+
+ private static final long serialVersionUID = -3704437769955257514L;
+
+ public RetryTimeoutException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/Retryable.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/Retryable.java
new file mode 100644
index 00000000..e80135dc
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/Retryable.java
@@ -0,0 +1,55 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+abstract class Retryable {
+
+ /**
+ * @throws TemporaryException for an error condition that can be temporary - i.e. retrying later could be successful
+ * @throws Exception for all other error conditions
+ */
+ protected abstract void attempt() throws TemporaryException, Exception;
+
+ public void execute(long interval, long timeout) throws RetryTimeoutException, Exception {
+ execute(0L, interval, timeout);
+ }
+
+ public void execute(long delay, long interval, long timeout) throws RetryTimeoutException, Exception {
+ long start = System.currentTimeMillis();
+ if (delay > 0L) {
+ sleep(delay);
+ }
+ while (true) {
+ try {
+ attempt();
+ return;
+ } catch (TemporaryException temporaryException) {
+ if (System.currentTimeMillis() - start < timeout) {
+ sleep(interval);
+ // continue
+ } else {
+ throw new RetryTimeoutException(temporaryException.getCause());
+ }
+ }
+ }
+ }
+
+ private void sleep(long millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException interruptedException) {
+ // continue
+ }
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/SuspendableThreadPoolExecutor.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/SuspendableThreadPoolExecutor.java
new file mode 100644
index 00000000..bf2afe81
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/SuspendableThreadPoolExecutor.java
@@ -0,0 +1,59 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+class SuspendableThreadPoolExecutor extends ThreadPoolExecutor {
+
+ private boolean available = false;
+ private ReentrantLock suspendLock = new ReentrantLock();
+ private Condition availableCondition = suspendLock.newCondition();
+
+ public SuspendableThreadPoolExecutor(ThreadFactory threadFactory) {
+ super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), threadFactory);
+ }
+
+ @Override
+ protected void beforeExecute(Thread thread, Runnable task) {
+ super.beforeExecute(thread, task);
+ suspendLock.lock();
+ try {
+ while (!available) {
+ availableCondition.await();
+ }
+ } catch (InterruptedException interruptedException) {
+ thread.interrupt();
+ } finally {
+ suspendLock.unlock();
+ }
+ }
+
+ public void setAvailable(boolean available) {
+ suspendLock.lock();
+ try {
+ this.available = available;
+ if (available) {
+ availableCondition.signalAll();
+ }
+ } finally {
+ suspendLock.unlock();
+ }
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/TemporaryException.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/TemporaryException.java
new file mode 100644
index 00000000..5ce6ac8f
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/TemporaryException.java
@@ -0,0 +1,32 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+import java.math.BigDecimal;
+
+/**
+ * Represents an error condition that can be temporary, i.e. that could go
+ * away by simply retrying the same operation after an interval.
+ */
+class TemporaryException extends Exception {
+
+ private static final long serialVersionUID = 7237380113208327295L;
+
+ public TemporaryException(Throwable cause) {
+ super(cause);
+ }
+
+ public static void main(String[] args) {
+ System.out.println(new BigDecimal("7412611111110.99"));
+ }
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/UnoUrl.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/UnoUrl.java
new file mode 100644
index 00000000..8134f265
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/UnoUrl.java
@@ -0,0 +1,62 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.office;
+
+/**
+ * Encapsulates the UNO Interprocess Connection type and parameters.
+ *
+ * OpenOffice.org supports two connection types: TCP sockets and named pipes.
+ * Named pipes are marginally faster and do not take up a TCP port, but they
+ * require native libraries, which means setting java.library.path
+ * when starting Java. E.g. on Linux
+ *
+ * java -Djava.library.path=/opt/openoffice.org/ure/lib ...
+ *
+ *
+ * See Opening a Connection
+ * in the OpenOffice.org Developer's Guide for more details.
+ */
+class UnoUrl {
+
+ private final String acceptString;
+ private final String connectString;
+
+ private UnoUrl(String acceptString, String connectString) {
+ this.acceptString = acceptString;
+ this.connectString = connectString;
+ }
+
+ public static UnoUrl socket(int port) {
+ String socketString = "socket,host=127.0.0.1,port=" + port;
+ return new UnoUrl(socketString, socketString + ",tcpNoDelay=1");
+ }
+
+ public static UnoUrl pipe(String pipeName) {
+ String pipeString = "pipe,name=" + pipeName;
+ return new UnoUrl(pipeString, pipeString);
+ }
+
+ public String getAcceptString() {
+ return acceptString;
+ }
+
+ public String getConnectString() {
+ return connectString;
+ }
+
+ @Override
+ public String toString() {
+ return connectString;
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/LinuxProcessManager.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/LinuxProcessManager.java
new file mode 100644
index 00000000..40d64e68
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/LinuxProcessManager.java
@@ -0,0 +1,82 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.process;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.IOUtils;
+
+/**
+ * {@link ProcessManager} implementation for Linux. Uses the ps
+ * and kill commands.
+ *
+ * Should Work on Solaris too, except that the command line string
+ * returned by ps there is limited to 80 characters and this affects
+ * {@link #findPid(String)}.
+ */
+public class LinuxProcessManager implements ProcessManager {
+
+ private static final Pattern PS_OUTPUT_LINE = Pattern.compile("^\\s*(\\d+)\\s+(.*)$");
+
+ private String[] runAsArgs;
+
+ public void setRunAsArgs(String... runAsArgs) {
+ this.runAsArgs = runAsArgs;
+ }
+
+ protected String[] psCommand() {
+ return new String[] { "/bin/ps", "-e", "-o", "pid,args" };
+ }
+
+ public long findPid(ProcessQuery query) throws IOException {
+ String regex = Pattern.quote(query.getCommand()) + ".*" + Pattern.quote(query.getArgument());
+ Pattern commandPattern = Pattern.compile(regex);
+ for (String line : execute(psCommand())) {
+ Matcher lineMatcher = PS_OUTPUT_LINE.matcher(line);
+ if (lineMatcher.matches()) {
+ String command = lineMatcher.group(2);
+ Matcher commandMatcher = commandPattern.matcher(command);
+ if (commandMatcher.find()) {
+ return Long.parseLong(lineMatcher.group(1));
+ }
+ }
+ }
+ return PID_NOT_FOUND;
+ }
+
+ public void kill(Process process, long pid) throws IOException {
+ if (pid <= 0) {
+ throw new IllegalArgumentException("invalid pid: " + pid);
+ }
+ execute("/bin/kill", "-KILL", Long.toString(pid));
+ }
+
+ private List execute(String... args) throws IOException {
+ String[] command;
+ if (runAsArgs != null) {
+ command = new String[runAsArgs.length + args.length];
+ System.arraycopy(runAsArgs, 0, command, 0, runAsArgs.length);
+ System.arraycopy(args, 0, command, runAsArgs.length, args.length);
+ } else {
+ command = args;
+ }
+ Process process = new ProcessBuilder(command).start();
+ @SuppressWarnings("unchecked")
+ List lines = IOUtils.readLines(process.getInputStream());
+ return lines;
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/ProcessManager.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/ProcessManager.java
new file mode 100644
index 00000000..bef1cac3
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/ProcessManager.java
@@ -0,0 +1,32 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.process;
+
+import java.io.IOException;
+
+public interface ProcessManager {
+
+ public static final long PID_NOT_FOUND = -2;
+ public static final long PID_UNKNOWN = -1;
+
+ void kill(Process process, long pid) throws IOException;
+
+ /**
+ * @param query
+ * @return the pid if found, {@link #PID_NOT_FOUND} if not,
+ * or {@link #PID_UNKNOWN} if this implementation is unable to find out
+ * @throws IOException
+ */
+ long findPid(ProcessQuery query) throws IOException;
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/ProcessQuery.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/ProcessQuery.java
new file mode 100644
index 00000000..3f9d1e00
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/ProcessQuery.java
@@ -0,0 +1,33 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.process;
+
+public class ProcessQuery {
+
+ private final String command;
+ private final String argument;
+
+ public ProcessQuery(String command, String argument) {
+ this.command = command;
+ this.argument = argument;
+ }
+
+ public String getCommand() {
+ return command;
+ }
+
+ public String getArgument() {
+ return argument;
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/PureJavaProcessManager.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/PureJavaProcessManager.java
new file mode 100644
index 00000000..83509160
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/PureJavaProcessManager.java
@@ -0,0 +1,25 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.process;
+
+public class PureJavaProcessManager implements ProcessManager {
+
+ public long findPid(ProcessQuery query) {
+ return PID_UNKNOWN;
+ }
+
+ public void kill(Process process, long pid) {
+ process.destroy();
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/SigarProcessManager.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/SigarProcessManager.java
new file mode 100644
index 00000000..c4e0851e
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/process/SigarProcessManager.java
@@ -0,0 +1,71 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.process;
+
+import java.io.IOException;
+
+import org.hyperic.sigar.Sigar;
+import org.hyperic.sigar.SigarException;
+import org.hyperic.sigar.ptql.ProcessFinder;
+
+/**
+ * {@link ProcessManager} implementation that uses the SIGAR library.
+ *
+ * Requires the sigar.jar in the classpath and the appropriate system-specific
+ * native library (e.g. libsigar-x86-linux.so on Linux x86) available
+ * in the java.library.path.
+ *
+ * See the SIGAR site
+ * for documentation and downloads.
+ */
+public class SigarProcessManager implements ProcessManager {
+
+ public long findPid(ProcessQuery query) throws IOException {
+ Sigar sigar = new Sigar();
+ try {
+ long[] pids = ProcessFinder.find(sigar, "State.Name.eq=" + query.getCommand());
+ for (int i = 0; i < pids.length; i++) {
+ String[] arguments = sigar.getProcArgs(pids[i]);
+ if (arguments != null && argumentMatches(arguments, query.getArgument())) {
+ return pids[i];
+ }
+ }
+ return PID_NOT_FOUND;
+ } catch (SigarException sigarException) {
+ throw new IOException("findPid failed", sigarException);
+ } finally {
+ sigar.close();
+ }
+ }
+
+ public void kill(Process process, long pid) throws IOException {
+ Sigar sigar = new Sigar();
+ try {
+ sigar.kill(pid, Sigar.getSigNum("KILL"));
+ } catch (SigarException sigarException) {
+ throw new IOException("kill failed", sigarException);
+ } finally {
+ sigar.close();
+ }
+ }
+
+ private boolean argumentMatches(String[] arguments, String expected) {
+ for (String argument : arguments) {
+ if (argument.contains(expected)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/util/PlatformUtils.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/util/PlatformUtils.java
new file mode 100644
index 00000000..9135eb71
--- /dev/null
+++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/util/PlatformUtils.java
@@ -0,0 +1,35 @@
+//
+// JODConverter - Java OpenDocument Converter
+// Copyright 2004-2012 Mirko Nasato and contributors
+//
+// JODConverter is Open Source software, you can redistribute it and/or
+// modify it under either (at your option) of the following licenses
+//
+// 1. The GNU Lesser General Public License v3 (or later)
+// -> http://www.gnu.org/licenses/lgpl-3.0.txt
+// 2. The Apache License, Version 2.0
+// -> http://www.apache.org/licenses/LICENSE-2.0.txt
+//
+package org.artofsolving.jodconverter.util;
+
+public class PlatformUtils {
+
+ private static final String OS_NAME = System.getProperty("os.name").toLowerCase();
+
+ private PlatformUtils() {
+ throw new AssertionError("utility class must not be instantiated");
+ }
+
+ public static boolean isLinux() {
+ return OS_NAME.startsWith("linux");
+ }
+
+ public static boolean isMac() {
+ return OS_NAME.startsWith("mac");
+ }
+
+ public static boolean isWindows() {
+ return OS_NAME.startsWith("windows");
+ }
+
+}
diff --git a/jodconverter-core/src/main/resources/document-formats.js b/jodconverter-core/src/main/resources/document-formats.js
new file mode 100644
index 00000000..f7f48bcf
--- /dev/null
+++ b/jodconverter-core/src/main/resources/document-formats.js
@@ -0,0 +1,188 @@
+[
+ {
+ "name": "Portable Document Format",
+ "extension": "pdf",
+ "mediaType": "application/pdf",
+ "storePropertiesByFamily": {
+ "DRAWING": {"FilterName": "draw_pdf_Export"},
+ "SPREADSHEET": {"FilterName": "calc_pdf_Export"},
+ "PRESENTATION": {"FilterName": "impress_pdf_Export"},
+ "TEXT": {"FilterName": "writer_pdf_Export"}
+ }
+ },
+ {
+ "name": "Macromedia Flash",
+ "extension": "swf",
+ "mediaType": "application/x-shockwave-flash",
+ "storePropertiesByFamily": {
+ "DRAWING": {"FilterName": "draw_flash_Export"},
+ "PRESENTATION": {"FilterName": "impress_flash_Export"}
+ }
+ },
+ {
+ "name": "HTML",
+ "extension": "html",
+ "mediaType": "text/html",
+ "inputFamily": "TEXT",
+ "storePropertiesByFamily": {
+ "SPREADSHEET": {"FilterName": "HTML (StarCalc)"},
+ "PRESENTATION": {"FilterName": "impress_html_Export"},
+ "TEXT": {"FilterName": "HTML (StarWriter)"}
+ }
+ },
+ {
+ "name": "OpenDocument Text",
+ "extension": "odt",
+ "mediaType": "application/vnd.oasis.opendocument.text",
+ "inputFamily": "TEXT",
+ "storePropertiesByFamily": {"TEXT": {"FilterName": "writer8"}}
+ },
+ {
+ "name": "OpenOffice.org 1.0 Text Document",
+ "extension": "sxw",
+ "mediaType": "application/vnd.sun.xml.writer",
+ "inputFamily": "TEXT",
+ "storePropertiesByFamily": {"TEXT": {"FilterName": "StarOffice XML (Writer)"}}
+ },
+ {
+ "name": "Microsoft Word",
+ "extension": "doc",
+ "mediaType": "application/msword",
+ "inputFamily": "TEXT",
+ "storePropertiesByFamily": {"TEXT": {"FilterName": "MS Word 97"}}
+ },
+ {
+ "name": "Microsoft Word 2007 XML",
+ "extension": "docx",
+ "mediaType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ "inputFamily": "TEXT"
+ },
+ {
+ "name": "Rich Text Format",
+ "extension": "rtf",
+ "mediaType": "text/rtf",
+ "inputFamily": "TEXT",
+ "storePropertiesByFamily": {"TEXT": {"FilterName": "Rich Text Format"}}
+ },
+ {
+ "name": "WordPerfect",
+ "extension": "wpd",
+ "mediaType": "application/wordperfect",
+ "inputFamily": "TEXT"
+ },
+ {
+ "name": "Plain Text",
+ "extension": "txt",
+ "mediaType": "text/plain",
+ "inputFamily": "TEXT",
+ "loadProperties": {
+ "FilterName": "Text (encoded)",
+ "FilterOptions": "utf8"
+ },
+ "storePropertiesByFamily": {"TEXT": {
+ "FilterName": "Text (encoded)",
+ "FilterOptions": "utf8"
+ }}
+ },
+ {
+ "name": "MediaWiki wikitext",
+ "extension": "wiki",
+ "mediaType": "text/x-wiki",
+ "storePropertiesByFamily": {"TEXT": {"FilterName": "MediaWiki"}}
+ },
+ {
+ "name": "OpenDocument Spreadsheet",
+ "extension": "ods",
+ "mediaType": "application/vnd.oasis.opendocument.spreadsheet",
+ "inputFamily": "SPREADSHEET",
+ "storePropertiesByFamily": {"SPREADSHEET": {"FilterName": "calc8"}}
+ },
+ {
+ "name": "OpenOffice.org 1.0 Spreadsheet",
+ "extension": "sxc",
+ "mediaType": "application/vnd.sun.xml.calc",
+ "inputFamily": "SPREADSHEET",
+ "storePropertiesByFamily": {"SPREADSHEET": {"FilterName": "StarOffice XML (Calc)"}}
+ },
+ {
+ "name": "Microsoft Excel",
+ "extension": "xls",
+ "mediaType": "application/vnd.ms-excel",
+ "inputFamily": "SPREADSHEET",
+ "storePropertiesByFamily": {"SPREADSHEET": {"FilterName": "MS Excel 97"}}
+ },
+ {
+ "name": "Microsoft Excel 2007 XML",
+ "extension": "xlsx",
+ "mediaType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ "inputFamily": "SPREADSHEET"
+ },
+ {
+ "name": "Comma Separated Values",
+ "extension": "csv",
+ "mediaType": "text/csv",
+ "inputFamily": "SPREADSHEET",
+ "loadProperties": {
+ "FilterName": "Text - txt - csv (StarCalc)",
+ "FilterOptions": "44,34,0"
+ },
+ "storePropertiesByFamily": {"SPREADSHEET": {
+ "FilterName": "Text - txt - csv (StarCalc)",
+ "FilterOptions": "44,34,0"
+ }}
+ },
+ {
+ "name": "Tab Separated Values",
+ "extension": "tsv",
+ "mediaType": "text/tab-separated-values",
+ "inputFamily": "SPREADSHEET",
+ "loadProperties": {
+ "FilterName": "Text - txt - csv (StarCalc)",
+ "FilterOptions": "9,34,0"
+ },
+ "storePropertiesByFamily": {"SPREADSHEET": {
+ "FilterName": "Text - txt - csv (StarCalc)",
+ "FilterOptions": "9,34,0"
+ }}
+ },
+ {
+ "name": "OpenDocument Presentation",
+ "extension": "odp",
+ "mediaType": "application/vnd.oasis.opendocument.presentation",
+ "inputFamily": "PRESENTATION",
+ "storePropertiesByFamily": {"PRESENTATION": {"FilterName": "impress8"}}
+ },
+ {
+ "name": "OpenOffice.org 1.0 Presentation",
+ "extension": "sxi",
+ "mediaType": "application/vnd.sun.xml.impress",
+ "inputFamily": "PRESENTATION",
+ "storePropertiesByFamily": {"PRESENTATION": {"FilterName": "StarOffice XML (Impress)"}}
+ },
+ {
+ "name": "Microsoft PowerPoint",
+ "extension": "ppt",
+ "mediaType": "application/vnd.ms-powerpoint",
+ "inputFamily": "PRESENTATION",
+ "storePropertiesByFamily": {"PRESENTATION": {"FilterName": "MS PowerPoint 97"}}
+ },
+ {
+ "name": "Microsoft PowerPoint 2007 XML",
+ "extension": "pptx",
+ "mediaType": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+ "inputFamily": "PRESENTATION"
+ },
+ {
+ "name": "OpenDocument Drawing",
+ "extension": "odg",
+ "mediaType": "application/vnd.oasis.opendocument.graphics",
+ "inputFamily": "DRAWING",
+ "storePropertiesByFamily": {"DRAWING": {"FilterName": "draw8"}}
+ },
+ {
+ "name": "Scalable Vector Graphics",
+ "extension": "svg",
+ "mediaType": "image/svg+xml",
+ "storePropertiesByFamily": {"DRAWING": {"FilterName": "draw_svg_Export"}}
+ }
+]
diff --git a/jodconverter-web/pom.xml b/jodconverter-web/pom.xml
new file mode 100644
index 00000000..ecca6943
--- /dev/null
+++ b/jodconverter-web/pom.xml
@@ -0,0 +1,171 @@
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 1.5.8.RELEASE
+
+
+ 4.0.0
+
+ jodconverter-web
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ 1.0-SNAPSHOT
+ ${basedir}/target/classes/logs
+ file-preview
+
+
+
+
+ jboss-public-repository-group
+ https://repository.jboss.org/nexus/content/groups/public-jboss/
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-freemarker
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ net.logstash.logback
+ logstash-logback-encoder
+ 4.11
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ com.yudianbank
+ jodconverter
+ 1.0-SNAPSHOT
+
+
+ commons-io
+ commons-io
+
+
+
+
+
+ org.redisson
+ redisson
+ 3.2.0
+
+
+ org.apache.poi
+ poi
+ 3.12
+
+
+ org.apache.poi
+ poi-scratchpad
+ 3.12
+
+
+ fr.opensagres.xdocreport
+ org.apache.poi.xwpf.converter.core
+ 1.0.5
+
+
+ poi
+ org.apache.poi
+
+
+
+
+ fr.opensagres.xdocreport
+ org.apache.poi.xwpf.converter.xhtml
+ 1.0.5
+
+
+ fr.opensagres.xdocreport
+ fr.opensagres.xdocreport.document
+ 1.0.5
+
+
+ commons-io
+ commons-io
+ 2.4
+
+
+
+ org.apache.commons
+ commons-compress
+ 1.9
+
+
+
+ com.github.junrar
+ junrar
+ 0.7
+
+
+ net.sourceforge.jchardet
+ jchardet
+ 1.0
+
+
+ antlr
+ antlr
+ 2.7.7
+
+
+ commons-httpclient
+ commons-httpclient
+ 3.1
+ test
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+ commons-cli
+ commons-cli
+ 1.2
+
+
+ com.thoughtworks.xstream
+ xstream
+ 1.3.1
+
+
+ com.google.guava
+ guava
+ 19.0
+
+
+
+
+
+ jodconverter-web/src/main/resources
+
+ **/*
+
+ true
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/com/yudianbank/FilePreviewApplication.java b/jodconverter-web/src/main/java/com/yudianbank/FilePreviewApplication.java
similarity index 77%
rename from src/main/java/com/yudianbank/FilePreviewApplication.java
rename to jodconverter-web/src/main/java/com/yudianbank/FilePreviewApplication.java
index 7fb84035..87ce7ed9 100644
--- a/src/main/java/com/yudianbank/FilePreviewApplication.java
+++ b/jodconverter-web/src/main/java/com/yudianbank/FilePreviewApplication.java
@@ -1,19 +1,14 @@
package com.yudianbank;
-import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
-import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.EnableScheduling;
-
-import java.util.Enumeration;
import java.util.Properties;
@SpringBootApplication
@EnableScheduling
@ComponentScan(value = "com.yudianbank.*")
-//@EnableApolloConfig
public class FilePreviewApplication {
public static void main(String[] args) {
Properties properties = System.getProperties();
diff --git a/src/main/java/com/yudianbank/config/RedissonConfig.java b/jodconverter-web/src/main/java/com/yudianbank/config/RedissonConfig.java
similarity index 100%
rename from src/main/java/com/yudianbank/config/RedissonConfig.java
rename to jodconverter-web/src/main/java/com/yudianbank/config/RedissonConfig.java
diff --git a/src/main/java/com/yudianbank/extend/ControlDocumentFormatRegistry.java b/jodconverter-web/src/main/java/com/yudianbank/extend/ControlDocumentFormatRegistry.java
similarity index 100%
rename from src/main/java/com/yudianbank/extend/ControlDocumentFormatRegistry.java
rename to jodconverter-web/src/main/java/com/yudianbank/extend/ControlDocumentFormatRegistry.java
diff --git a/src/main/java/com/yudianbank/filters/ChinesePathFilter.java b/jodconverter-web/src/main/java/com/yudianbank/filters/ChinesePathFilter.java
similarity index 100%
rename from src/main/java/com/yudianbank/filters/ChinesePathFilter.java
rename to jodconverter-web/src/main/java/com/yudianbank/filters/ChinesePathFilter.java
diff --git a/src/main/java/com/yudianbank/filters/FilterConfiguration.java b/jodconverter-web/src/main/java/com/yudianbank/filters/FilterConfiguration.java
similarity index 100%
rename from src/main/java/com/yudianbank/filters/FilterConfiguration.java
rename to jodconverter-web/src/main/java/com/yudianbank/filters/FilterConfiguration.java
diff --git a/src/main/java/com/yudianbank/param/ReturnResponse.java b/jodconverter-web/src/main/java/com/yudianbank/param/ReturnResponse.java
similarity index 100%
rename from src/main/java/com/yudianbank/param/ReturnResponse.java
rename to jodconverter-web/src/main/java/com/yudianbank/param/ReturnResponse.java
diff --git a/src/main/java/com/yudianbank/utils/ConverterUtils.java b/jodconverter-web/src/main/java/com/yudianbank/utils/ConverterUtils.java
similarity index 100%
rename from src/main/java/com/yudianbank/utils/ConverterUtils.java
rename to jodconverter-web/src/main/java/com/yudianbank/utils/ConverterUtils.java
diff --git a/src/main/java/com/yudianbank/utils/DeleteFileUtil.java b/jodconverter-web/src/main/java/com/yudianbank/utils/DeleteFileUtil.java
similarity index 100%
rename from src/main/java/com/yudianbank/utils/DeleteFileUtil.java
rename to jodconverter-web/src/main/java/com/yudianbank/utils/DeleteFileUtil.java
diff --git a/src/main/java/com/yudianbank/utils/DownloadUtils.java b/jodconverter-web/src/main/java/com/yudianbank/utils/DownloadUtils.java
similarity index 100%
rename from src/main/java/com/yudianbank/utils/DownloadUtils.java
rename to jodconverter-web/src/main/java/com/yudianbank/utils/DownloadUtils.java
diff --git a/src/main/java/com/yudianbank/utils/FileUtils.java b/jodconverter-web/src/main/java/com/yudianbank/utils/FileUtils.java
similarity index 100%
rename from src/main/java/com/yudianbank/utils/FileUtils.java
rename to jodconverter-web/src/main/java/com/yudianbank/utils/FileUtils.java
diff --git a/src/main/java/com/yudianbank/utils/OfficeToPdf.java b/jodconverter-web/src/main/java/com/yudianbank/utils/OfficeToPdf.java
similarity index 98%
rename from src/main/java/com/yudianbank/utils/OfficeToPdf.java
rename to jodconverter-web/src/main/java/com/yudianbank/utils/OfficeToPdf.java
index 0a3efa63..f27f4979 100644
--- a/src/main/java/com/yudianbank/utils/OfficeToPdf.java
+++ b/jodconverter-web/src/main/java/com/yudianbank/utils/OfficeToPdf.java
@@ -1,7 +1,6 @@
package com.yudianbank.utils;
import org.artofsolving.jodconverter.OfficeDocumentConverter;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.File;
diff --git a/src/main/java/com/yudianbank/utils/PoiExcelToHtml.java b/jodconverter-web/src/main/java/com/yudianbank/utils/PoiExcelToHtml.java
similarity index 100%
rename from src/main/java/com/yudianbank/utils/PoiExcelToHtml.java
rename to jodconverter-web/src/main/java/com/yudianbank/utils/PoiExcelToHtml.java
diff --git a/src/main/java/com/yudianbank/utils/ShedulerClean.java b/jodconverter-web/src/main/java/com/yudianbank/utils/ShedulerClean.java
similarity index 84%
rename from src/main/java/com/yudianbank/utils/ShedulerClean.java
rename to jodconverter-web/src/main/java/com/yudianbank/utils/ShedulerClean.java
index 09ce9e1e..f22dbb42 100644
--- a/src/main/java/com/yudianbank/utils/ShedulerClean.java
+++ b/jodconverter-web/src/main/java/com/yudianbank/utils/ShedulerClean.java
@@ -1,11 +1,8 @@
package com.yudianbank.utils;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
-import java.io.File;
-
@Component
public class ShedulerClean {
@Value("${file.dir}")
diff --git a/src/main/java/com/yudianbank/utils/SimTextUtil.java b/jodconverter-web/src/main/java/com/yudianbank/utils/SimTextUtil.java
similarity index 100%
rename from src/main/java/com/yudianbank/utils/SimTextUtil.java
rename to jodconverter-web/src/main/java/com/yudianbank/utils/SimTextUtil.java
diff --git a/src/main/java/com/yudianbank/utils/WordToHtml.java b/jodconverter-web/src/main/java/com/yudianbank/utils/WordToHtml.java
similarity index 100%
rename from src/main/java/com/yudianbank/utils/WordToHtml.java
rename to jodconverter-web/src/main/java/com/yudianbank/utils/WordToHtml.java
diff --git a/src/main/java/com/yudianbank/utils/ZipReader.java b/jodconverter-web/src/main/java/com/yudianbank/utils/ZipReader.java
similarity index 100%
rename from src/main/java/com/yudianbank/utils/ZipReader.java
rename to jodconverter-web/src/main/java/com/yudianbank/utils/ZipReader.java
diff --git a/src/main/java/com/yudianbank/web/controller/FileController.java b/jodconverter-web/src/main/java/com/yudianbank/web/controller/FileController.java
similarity index 100%
rename from src/main/java/com/yudianbank/web/controller/FileController.java
rename to jodconverter-web/src/main/java/com/yudianbank/web/controller/FileController.java
diff --git a/src/main/java/com/yudianbank/web/controller/OnlinePreviewController.java b/jodconverter-web/src/main/java/com/yudianbank/web/controller/OnlinePreviewController.java
similarity index 98%
rename from src/main/java/com/yudianbank/web/controller/OnlinePreviewController.java
rename to jodconverter-web/src/main/java/com/yudianbank/web/controller/OnlinePreviewController.java
index d25898b9..7621a137 100644
--- a/src/main/java/com/yudianbank/web/controller/OnlinePreviewController.java
+++ b/jodconverter-web/src/main/java/com/yudianbank/web/controller/OnlinePreviewController.java
@@ -1,7 +1,5 @@
package com.yudianbank.web.controller;
-import com.ctrip.framework.apollo.Config;
-import com.ctrip.framework.apollo.spring.annotation.ApolloConfig;
import com.yudianbank.param.ReturnResponse;
import com.yudianbank.utils.*;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/src/main/resources/META-INF/app.properties b/jodconverter-web/src/main/resources/META-INF/app.properties
similarity index 100%
rename from src/main/resources/META-INF/app.properties
rename to jodconverter-web/src/main/resources/META-INF/app.properties
diff --git a/src/main/resources/application.properties b/jodconverter-web/src/main/resources/application.properties
similarity index 100%
rename from src/main/resources/application.properties
rename to jodconverter-web/src/main/resources/application.properties
diff --git a/src/main/resources/logback.xml b/jodconverter-web/src/main/resources/logback.xml
similarity index 100%
rename from src/main/resources/logback.xml
rename to jodconverter-web/src/main/resources/logback.xml
diff --git a/src/main/resources/static/config.js b/jodconverter-web/src/main/resources/static/config.js
similarity index 100%
rename from src/main/resources/static/config.js
rename to jodconverter-web/src/main/resources/static/config.js
diff --git a/src/main/resources/static/css/img/diy/1_close.png b/jodconverter-web/src/main/resources/static/css/img/diy/1_close.png
similarity index 100%
rename from src/main/resources/static/css/img/diy/1_close.png
rename to jodconverter-web/src/main/resources/static/css/img/diy/1_close.png
diff --git a/src/main/resources/static/css/img/diy/1_open.png b/jodconverter-web/src/main/resources/static/css/img/diy/1_open.png
similarity index 100%
rename from src/main/resources/static/css/img/diy/1_open.png
rename to jodconverter-web/src/main/resources/static/css/img/diy/1_open.png
diff --git a/src/main/resources/static/css/img/diy/2.png b/jodconverter-web/src/main/resources/static/css/img/diy/2.png
similarity index 100%
rename from src/main/resources/static/css/img/diy/2.png
rename to jodconverter-web/src/main/resources/static/css/img/diy/2.png
diff --git a/src/main/resources/static/css/img/diy/3.png b/jodconverter-web/src/main/resources/static/css/img/diy/3.png
similarity index 100%
rename from src/main/resources/static/css/img/diy/3.png
rename to jodconverter-web/src/main/resources/static/css/img/diy/3.png
diff --git a/src/main/resources/static/css/img/diy/4.png b/jodconverter-web/src/main/resources/static/css/img/diy/4.png
similarity index 100%
rename from src/main/resources/static/css/img/diy/4.png
rename to jodconverter-web/src/main/resources/static/css/img/diy/4.png
diff --git a/src/main/resources/static/css/img/diy/5.png b/jodconverter-web/src/main/resources/static/css/img/diy/5.png
similarity index 100%
rename from src/main/resources/static/css/img/diy/5.png
rename to jodconverter-web/src/main/resources/static/css/img/diy/5.png
diff --git a/src/main/resources/static/css/img/diy/6.png b/jodconverter-web/src/main/resources/static/css/img/diy/6.png
similarity index 100%
rename from src/main/resources/static/css/img/diy/6.png
rename to jodconverter-web/src/main/resources/static/css/img/diy/6.png
diff --git a/src/main/resources/static/css/img/diy/7.png b/jodconverter-web/src/main/resources/static/css/img/diy/7.png
similarity index 100%
rename from src/main/resources/static/css/img/diy/7.png
rename to jodconverter-web/src/main/resources/static/css/img/diy/7.png
diff --git a/src/main/resources/static/css/img/diy/8.png b/jodconverter-web/src/main/resources/static/css/img/diy/8.png
similarity index 100%
rename from src/main/resources/static/css/img/diy/8.png
rename to jodconverter-web/src/main/resources/static/css/img/diy/8.png
diff --git a/src/main/resources/static/css/img/diy/9.png b/jodconverter-web/src/main/resources/static/css/img/diy/9.png
similarity index 100%
rename from src/main/resources/static/css/img/diy/9.png
rename to jodconverter-web/src/main/resources/static/css/img/diy/9.png
diff --git a/src/main/resources/static/css/img/line_conn.gif b/jodconverter-web/src/main/resources/static/css/img/line_conn.gif
similarity index 100%
rename from src/main/resources/static/css/img/line_conn.gif
rename to jodconverter-web/src/main/resources/static/css/img/line_conn.gif
diff --git a/src/main/resources/static/css/img/loading.gif b/jodconverter-web/src/main/resources/static/css/img/loading.gif
similarity index 100%
rename from src/main/resources/static/css/img/loading.gif
rename to jodconverter-web/src/main/resources/static/css/img/loading.gif
diff --git a/src/main/resources/static/css/img/zTreeStandard.gif b/jodconverter-web/src/main/resources/static/css/img/zTreeStandard.gif
similarity index 100%
rename from src/main/resources/static/css/img/zTreeStandard.gif
rename to jodconverter-web/src/main/resources/static/css/img/zTreeStandard.gif
diff --git a/src/main/resources/static/css/img/zTreeStandard.png b/jodconverter-web/src/main/resources/static/css/img/zTreeStandard.png
similarity index 100%
rename from src/main/resources/static/css/img/zTreeStandard.png
rename to jodconverter-web/src/main/resources/static/css/img/zTreeStandard.png
diff --git a/src/main/resources/static/css/loading.css b/jodconverter-web/src/main/resources/static/css/loading.css
similarity index 100%
rename from src/main/resources/static/css/loading.css
rename to jodconverter-web/src/main/resources/static/css/loading.css
diff --git a/src/main/resources/static/css/viewer.min.css b/jodconverter-web/src/main/resources/static/css/viewer.min.css
similarity index 100%
rename from src/main/resources/static/css/viewer.min.css
rename to jodconverter-web/src/main/resources/static/css/viewer.min.css
diff --git a/src/main/resources/static/css/zTreeStyle.css b/jodconverter-web/src/main/resources/static/css/zTreeStyle.css
similarity index 93%
rename from src/main/resources/static/css/zTreeStyle.css
rename to jodconverter-web/src/main/resources/static/css/zTreeStyle.css
index 4a1705b1..978a436c 100644
--- a/src/main/resources/static/css/zTreeStyle.css
+++ b/jodconverter-web/src/main/resources/static/css/zTreeStyle.css
@@ -12,7 +12,7 @@ website: http://code.google.com/p/jquerytree/
.ztree {margin:0; padding:5px; color:#333}
.ztree li{padding:0; margin:0; list-style:none; line-height:14px; text-align:left; white-space:nowrap; outline:0}
.ztree li ul{ margin:0; padding:0 0 0 18px}
-.ztree li ul.line{ background:url(./img/line_conn.gif) 0 0 repeat-y;}
+.ztree li ul.line{ background:url(img/line_conn.gif) 0 0 repeat-y;}
.ztree li a {padding:1px 3px 0 0; margin:0; cursor:pointer; height:17px; color:#333; background-color: transparent;
text-decoration:none; vertical-align:top; display: inline-block}
@@ -29,7 +29,7 @@ website: http://code.google.com/p/jquerytree/
.ztree li span.button {line-height:0; margin:0; width:16px; height:16px; display: inline-block; vertical-align:middle;
border:0 none; cursor: pointer;outline:none;
background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
- background-image:url("./img/zTreeStandard.png"); *background-image:url("./img/zTreeStandard.gif")}
+ background-image:url("img/zTreeStandard.png"); *background-image:url("img/zTreeStandard.gif")}
.ztree li span.button.chk {width:13px; height:13px; margin:0 3px 0 0; cursor: auto}
.ztree li span.button.chk.checkbox_false_full {background-position:0 0}
@@ -76,13 +76,13 @@ website: http://code.google.com/p/jquerytree/
.ztree li span.button.edit {margin-right:2px; background-position:-110px -48px; vertical-align:top; *vertical-align:middle}
.ztree li span.button.remove {margin-right:2px; background-position:-110px -64px; vertical-align:top; *vertical-align:middle}
-.ztree li span.button.ico_loading{margin-right:2px; background:url(./img/loading.gif) no-repeat scroll 0 0 transparent; vertical-align:top; *vertical-align:middle}
+.ztree li span.button.ico_loading{margin-right:2px; background:url(img/loading.gif) no-repeat scroll 0 0 transparent; vertical-align:top; *vertical-align:middle}
ul.tmpTargetzTree {background-color:#FFE6B0; opacity:0.8; filter:alpha(opacity=80)}
span.tmpzTreeMove_arrow {width:16px; height:16px; display: inline-block; padding:0; margin:2px 0 0 1px; border:0 none; position:absolute;
background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
- background-position:-110px -80px; background-image:url("./img/zTreeStandard.png"); *background-image:url("./img/zTreeStandard.gif")}
+ background-position:-110px -80px; background-image:url("img/zTreeStandard.png"); *background-image:url("img/zTreeStandard.gif")}
ul.ztree.zTreeDragUL {margin:0; padding:0; position:absolute; width:auto; height:auto;overflow:hidden; background-color:#cfcfcf; border:1px #00B83F dotted; opacity:0.8; filter:alpha(opacity=80)}
.zTreeMask {z-index:10000; background-color:#cfcfcf; opacity:0.0; filter:alpha(opacity=0); position:absolute}
diff --git a/src/main/resources/static/images/sorry.jpg b/jodconverter-web/src/main/resources/static/images/sorry.jpg
similarity index 100%
rename from src/main/resources/static/images/sorry.jpg
rename to jodconverter-web/src/main/resources/static/images/sorry.jpg
diff --git a/src/main/resources/static/index.html b/jodconverter-web/src/main/resources/static/index.html
similarity index 100%
rename from src/main/resources/static/index.html
rename to jodconverter-web/src/main/resources/static/index.html
diff --git a/src/main/resources/static/js/excel.header.js b/jodconverter-web/src/main/resources/static/js/excel.header.js
similarity index 100%
rename from src/main/resources/static/js/excel.header.js
rename to jodconverter-web/src/main/resources/static/js/excel.header.js
diff --git a/src/main/resources/static/js/jquery-3.0.0.min.js b/jodconverter-web/src/main/resources/static/js/jquery-3.0.0.min.js
similarity index 100%
rename from src/main/resources/static/js/jquery-3.0.0.min.js
rename to jodconverter-web/src/main/resources/static/js/jquery-3.0.0.min.js
diff --git a/src/main/resources/static/js/jquery.ztree.core.js b/jodconverter-web/src/main/resources/static/js/jquery.ztree.core.js
similarity index 100%
rename from src/main/resources/static/js/jquery.ztree.core.js
rename to jodconverter-web/src/main/resources/static/js/jquery.ztree.core.js
diff --git a/src/main/resources/static/js/viewer.min.js b/jodconverter-web/src/main/resources/static/js/viewer.min.js
similarity index 100%
rename from src/main/resources/static/js/viewer.min.js
rename to jodconverter-web/src/main/resources/static/js/viewer.min.js
diff --git a/src/main/resources/web/compress.ftl b/jodconverter-web/src/main/resources/web/compress.ftl
similarity index 100%
rename from src/main/resources/web/compress.ftl
rename to jodconverter-web/src/main/resources/web/compress.ftl
diff --git a/src/main/resources/web/doc.ftl b/jodconverter-web/src/main/resources/web/doc.ftl
similarity index 100%
rename from src/main/resources/web/doc.ftl
rename to jodconverter-web/src/main/resources/web/doc.ftl
diff --git a/src/main/resources/web/docxConvert.ftl b/jodconverter-web/src/main/resources/web/docxConvert.ftl
similarity index 100%
rename from src/main/resources/web/docxConvert.ftl
rename to jodconverter-web/src/main/resources/web/docxConvert.ftl
diff --git a/src/main/resources/web/fileNotSupported.ftl b/jodconverter-web/src/main/resources/web/fileNotSupported.ftl
similarity index 100%
rename from src/main/resources/web/fileNotSupported.ftl
rename to jodconverter-web/src/main/resources/web/fileNotSupported.ftl
diff --git a/src/main/resources/web/pdf.ftl b/jodconverter-web/src/main/resources/web/pdf.ftl
similarity index 100%
rename from src/main/resources/web/pdf.ftl
rename to jodconverter-web/src/main/resources/web/pdf.ftl
diff --git a/src/main/resources/web/picture.ftl b/jodconverter-web/src/main/resources/web/picture.ftl
similarity index 100%
rename from src/main/resources/web/picture.ftl
rename to jodconverter-web/src/main/resources/web/picture.ftl
diff --git a/src/main/resources/web/txt.ftl b/jodconverter-web/src/main/resources/web/txt.ftl
similarity index 100%
rename from src/main/resources/web/txt.ftl
rename to jodconverter-web/src/main/resources/web/txt.ftl
diff --git a/src/test/java/com/yudianbank/FilePreviewApplicationTests.java b/jodconverter-web/src/test/java/com/yudianbank/FilePreviewApplicationTests.java
similarity index 100%
rename from src/test/java/com/yudianbank/FilePreviewApplicationTests.java
rename to jodconverter-web/src/test/java/com/yudianbank/FilePreviewApplicationTests.java
diff --git a/pom.xml b/pom.xml
index 8e8a8590..03468e2b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,166 +1,17 @@
- 4.0.0
-
- com.yudianbank
- filepreview
- 0.0.1-SNAPSHOT
- jar
-
- file-online-preview
- Demo project for Spring Boot
-
-
- org.springframework.boot
- spring-boot-starter-parent
- 1.5.8.RELEASE
-
-
-
-
- UTF-8
- UTF-8
- 1.8
- 1.0-SNAPSHOT
- ${basedir}/target/classes/logs
- file-preview
-
-
-
-
- org.springframework.boot
- spring-boot-starter-freemarker
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- net.logstash.logback
- logstash-logback-encoder
- 4.11
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
-
- org.redisson
- redisson
- 3.2.0
-
-
- org.apache.poi
- poi
- 3.12
-
-
- org.apache.poi
- poi-scratchpad
- 3.12
-
-
- fr.opensagres.xdocreport
- org.apache.poi.xwpf.converter.core
- 1.0.5
-
-
- poi
- org.apache.poi
-
-
-
-
- fr.opensagres.xdocreport
- org.apache.poi.xwpf.converter.xhtml
- 1.0.5
-
-
- fr.opensagres.xdocreport
- fr.opensagres.xdocreport.document
- 1.0.5
-
-
- commons-io
- commons-io
- 2.4
-
-
-
- org.apache.commons
- commons-compress
- 1.9
-
-
-
- com.github.junrar
- junrar
- 0.7
-
-
- net.sourceforge.jchardet
- jchardet
- 1.0
-
-
- antlr
- antlr
- 2.7.7
-
-
- commons-httpclient
- commons-httpclient
- 3.1
- test
-
-
- commons-logging
- commons-logging
-
-
-
-
- org.artofsolving.jodconverter
- jodconverter-core
- 3.0-SNAPSHOT
-
-
- commons-io
- commons-io
-
-
-
-
- commons-cli
- commons-cli
- 1.2
-
-
- com.thoughtworks.xstream
- xstream
- 1.3.1
-
-
-
-
-
- ${basedir}/src/main/resources
-
- **/*
-
- true
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
+ com.yudianbank
+ filepreview
+ 0.0.1-SNAPSHOT
+
+ jodconverter-core
+ jodconverter-web
+
+ pom
+ file-online-preview
+ Demo project for Spring Boot