diff --git a/jodconverter-core/pom.xml b/jodconverter-core/pom.xml new file mode 100644 index 00000000..258a5031 --- /dev/null +++ b/jodconverter-core/pom.xml @@ -0,0 +1,160 @@ + + + 4.0.0 + com.yudianbank + jodconverter-core + 1.0-SNAPSHOT + jar + + UTF-8 + + + + + jboss-public-repository-group + https://repository.jboss.org/nexus/content/groups/public-jboss/ + + + + + + commons-io + commons-io + 1.4 + + + org.openoffice + juh + 3.2.1 + + + org.openoffice + ridl + 3.2.1 + + + org.openoffice + unoil + 3.2.1 + + + + commons-cli + commons-cli + 1.1 + true + + + org.hyperic + sigar + 1.6.5.132 + true + + + org.json + json + 20090211 + true + + + org.testng + testng + 6.0.1 + test + + + + + + + + maven-source-plugin + 2.1 + + true + + + + compile + + jar + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.6 + 1.6 + UTF-8 + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.7.2 + + + false + 1 + + + + org.apache.maven.plugins + maven-jar-plugin + 2.3.1 + + + + org.artofsolving.jodconverter.cli.Convert + true + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.2-beta-5 + + + src/main/assembly/dist.xml + + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.7 + + + org.codehaus.mojo + cobertura-maven-plugin + 2.4 + + + + + + + repo + User Project Releases + http://192.168.1.204:8081/nexus/content/repositories/releases + + + repo + User Project SNAPSHOTS + http://192.168.1.204:8081/nexus/content/repositories/snapshots + + + diff --git a/jodconverter-core/src/main/assembly/dist.xml b/jodconverter-core/src/main/assembly/dist.xml new file mode 100644 index 00000000..40aaf059 --- /dev/null +++ b/jodconverter-core/src/main/assembly/dist.xml @@ -0,0 +1,37 @@ + + dist + + zip + + + + lib + + org.hyperic:sigar + + + + + + + LICENSE.txt + README.txt + + + + target + / + + ${project.artifactId}-${project.version}-javadoc.jar + ${project.artifactId}-${project.version}-sources.jar + + + + src/main/resources + /conf + + document-formats.js + + + + \ No newline at end of file diff --git a/jodconverter-core/src/main/java/org/artofsolving/jodconverter/AbstractConversionTask.java b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/AbstractConversionTask.java new file mode 100644 index 00000000..f7242612 --- /dev/null +++ b/jodconverter-core/src/main/java/org/artofsolving/jodconverter/AbstractConversionTask.java @@ -0,0 +1,125 @@ +// +// 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.SERVICE_DESKTOP; +import static org.artofsolving.jodconverter.office.OfficeUtils.cast; +import static org.artofsolving.jodconverter.office.OfficeUtils.toUnoProperties; +import static org.artofsolving.jodconverter.office.OfficeUtils.toUrl; + +import java.io.File; +import java.util.Map; + +import org.artofsolving.jodconverter.office.OfficeContext; +import org.artofsolving.jodconverter.office.OfficeException; +import org.artofsolving.jodconverter.office.OfficeTask; + +import com.sun.star.frame.XComponentLoader; +import com.sun.star.frame.XStorable; +import com.sun.star.io.IOException; +import com.sun.star.lang.IllegalArgumentException; +import com.sun.star.lang.XComponent; +import com.sun.star.task.ErrorCodeIOException; +import com.sun.star.util.CloseVetoException; +import com.sun.star.util.XCloseable; + +public abstract class AbstractConversionTask implements OfficeTask { + + private final File inputFile; + private final File outputFile; + + public AbstractConversionTask(File inputFile, File outputFile) { + this.inputFile = inputFile; + this.outputFile = outputFile; + } + + protected abstract Map getLoadProperties(File inputFile); + + protected abstract Map getStoreProperties(File outputFile, XComponent document); + + public void execute(OfficeContext context) throws OfficeException { + XComponent document = null; + try { + document = loadDocument(context, inputFile); + modifyDocument(document); + storeDocument(document, outputFile); + } catch (OfficeException officeException) { + throw officeException; + } catch (Exception exception) { + throw new OfficeException("conversion failed", exception); + } finally { + if (document != null) { + XCloseable closeable = cast(XCloseable.class, document); + if (closeable != null) { + try { + closeable.close(true); + } catch (CloseVetoException closeVetoException) { + // whoever raised the veto should close the document + } + } else { + document.dispose(); + } + } + } + } + + private XComponent loadDocument(OfficeContext context, File inputFile) throws OfficeException { + if (!inputFile.exists()) { + throw new OfficeException("input document not found"); + } + XComponentLoader loader = cast(XComponentLoader.class, context.getService(SERVICE_DESKTOP)); + Map 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