为啥 libreoffice sdk 在通过 Web 服务同时创建文档时会崩溃?

Posted

技术标签:

【中文标题】为啥 libreoffice sdk 在通过 Web 服务同时创建文档时会崩溃?【英文标题】:why libreoffice sdk crash when simultaneosly and concurrently create documents through a webservice?为什么 libreoffice sdk 在通过 Web 服务同时创建文档时会崩溃? 【发布时间】:2019-04-10 21:51:50 【问题描述】:

我正在运行一个网络服务,它替换 docx 模板中的文本,然后将其转换为 pdf。 我正在使用 ubuntu 18.04 和 glassfish 服务器进行部署 当我对转换一切的服务提出单一请求时,一切正常,但是 当我像双击问题或并发请求一样快速发出双重请求时,我得到了这个异常:

com.sun.star.lang.DisposedException 在 com.sun.star.lib.uno.environments.remote.JobQueue.removeJob(JobQueue.java:201) . . aused by: java.io.IOException: EOF达到 - socket,host=localhost,port=8100,localHost=localhost,localPort=58494,peerHost=localhost,peerPort=8100 在 com.sun.star.lib.uno.bridges.java_remote.XConnectionInputStream_Adapter.read(XConnectionInputStream_Adapter.java:50)

我根据示例构建代码,我是 openoffice 和 LibreOffice 的初学者,我看到异常行指向 xDesktop.terminate(); ,所以我做了一个实验并删除了该声明,所以现在没有引发异常,但是正如我提到的我是一个初学者,所以我不确定 xDesktop.terminate();删除它会有什么后果?

这是我正在运行的代码:

    public Response getFilePdf(Integer idqueja)        
        try 

                    // Initialise
       String oooExeFolder = "/opt/libreoffice6.1/program";
       XComponentContext xContext = BootstrapSocketConnector.bootstrap(oooExeFolder);
    //XComponentContext xContext = Bootstrap.bootstrap();

    XMultiComponentFactory xMCF = xContext.getServiceManager();

    Object oDesktop = xMCF.createInstanceWithContext(
         "com.sun.star.frame.Desktop", xContext);

    XDesktop xDesktop = (XDesktop) UnoRuntime.queryInterface(
         XDesktop.class, oDesktop);

    // Load the Document
    String workingDir = "/home/somePath/";
    String myTemplate = workingDir + "template.docx";

    if (!new File(myTemplate).canRead()) 
        throw new RuntimeException("Cannotix load template:" + new File(myTemplate));
    

    XComponentLoader xCompLoader = (XComponentLoader) UnoRuntime
        .queryInterface(com.sun.star.frame.XComponentLoader.class, xDesktop);

    String sUrl = "file:///" + myTemplate;

    PropertyValue[] propertyValues = new PropertyValue[0];

    propertyValues = new PropertyValue[1];
    propertyValues[0] = new PropertyValue();
    propertyValues[0].Name = "Hidden";
    propertyValues[0].Value = new Boolean(true);

    XComponent xComp = xCompLoader.loadComponentFromURL(
        sUrl, "_blank", 0, propertyValues);


    // Manipulate
    XReplaceDescriptor xReplaceDescr = null;
    XReplaceable xReplaceable = null;

    XTextDocument xTextDocument = (XTextDocument) UnoRuntime
            .queryInterface(XTextDocument.class, xComp);

    xReplaceable = (XReplaceable) UnoRuntime
            .queryInterface(XReplaceable.class,
                    xTextDocument);

    xReplaceDescr = (XReplaceDescriptor) xReplaceable
            .createReplaceDescriptor();


        xReplaceDescr.setSearchString("<version>");
    xReplaceDescr.setReplaceString("1.x");
    xReplaceable.replaceAll(xReplaceDescr);
    // mail merge the date
    xReplaceDescr.setSearchString("<number>");
    xReplaceDescr.setReplaceString("12345677");
    xReplaceable.replaceAll(xReplaceDescr);


        OOoOutputStream output= new OOoOutputStream();

    // save as a PDF 
    XStorable xStorable = (XStorable) UnoRuntime
            .queryInterface(XStorable.class, xComp);

    propertyValues = new PropertyValue[2];
    // Setting the flag for overwriting
    propertyValues[0] = new PropertyValue();
        propertyValues[1] = new PropertyValue();

    propertyValues[0].Name = "OutputStream";
    propertyValues[0].Value = output;
    // Setting the filter name

    propertyValues[1].Name = "FilterName";
    propertyValues[1].Value = "writer_pdf_Export";

    // Appending the favoured extension to the origin document name
    //String myResult = workingDir + "fileConverted.pdf";
    xStorable.storeToURL("private:stream", propertyValues);


    // shutdown
    xDesktop.terminate();

         ByteArrayInputStream inStream = new ByteArrayInputStream(output.toByteArray());



                  ResponseBuilder response = Response.ok((Object) inStream);
                            response.header("Content-Disposition", "attachment;filename=template.pdf");
                            return response.build();   


         catch (Exception e) 
            e.printStackTrace();
                        ResponseBuilder response = Response.serverError();
                        return response.build();
               
    

所以这个 webservice 方法计划为大量用户提供文档,所以如果我同时收到请愿书或太连续它会引发异常,除非我删除 xDesktop.terminate();但我不知道它是否会产生进一步的后果,比如覆盖内存或类似的事情。 提前致谢。

【问题讨论】:

【参考方案1】:

问题在于 xDesktop.terminate() 请求关闭底层 soffice 进程(Java API 基本上只是它的一个包装器)。你有两个选择:

    要么自己预先启动 libreoffice,因此您不需要一直生成新的 soffice 进程,而只需连接到现有的进程即可。这样做的好处是,如果您有很多小请求(没有启动成本),性能会很好,但是您的转换不会太孤立(安全问题),并且实际上转换不会并行发生。在这种情况下,您启动了 soffice 进程,所以不要调用 xDesktop.terminate()。

    或者您使用专用的、唯一的 UserInstallation 目录启动每个 soffice 进程(请参阅 libreoffice --help),然后 xDesktop.terminate() 不会有问题,因为一次转换是一个 soffice 进程。

DisposedException 仅表示您正在使用远程 UNO 协议与 soffice 进程交谈,并且在您的请求正在进行时,其他人杀死了 soffice 进程。

您需要研究 Java API 如何允许传递自定义 soffice 参数,但在最坏的情况下,您可以执行 soffice "--accept=socket,host=localhost,port=9999;urp;StarOffice.ServiceManager" -env:UserInstallation=file:///tmp/test 之类的操作,如果您进行两次转换,您需要确保 9999 和 /tmp/test 是唯一的在平行下。 (同样,请参阅文档,如果您觉得更好,可以使用 unix 套接字而不是 TCP 端口。)

所以底线:如果您在多个转换之间共享 soffice 进程,则不要终止 xDesktop 单例,因为这会“崩溃”其他转换进程。

【讨论】:

感谢您的回答,正如您所说,我必须研究 java api,但这绝对是一个很好的选择,您能否在 选项 1 中解释一下,如何它会成为安全问题吗? 实际上,如果您使用选项 1,第一个版本可能不会终止 xDesktop。那将意味着它是懒惰的,并且从未被杀死。关于安全性,根据您的用例,一个转换失败可能是一个问题,因为另一个转换在文件导入或导出期间设法使 soffice 进程崩溃(由于 LibreOffice 错误)。你需要考虑这一点,也许这是可以接受的,也许你认为这是一个 DoS 并且它是一个安全问题。 嗨! ,只是这个问题的更新,我删除了 xDesktop.terminate() ,我没有得到 DisposedException 了,但我意识到生成的文档在内存中保持打开状态,所以做了一些研究我发现:*XCloseable xcloseable = (XCloseable) UnoRuntime.queryInterface(XCloseable.class, document); xcloseable.close(false);,这个只是关闭文档,而不是整个soffice进程。

以上是关于为啥 libreoffice sdk 在通过 Web 服务同时创建文档时会崩溃?的主要内容,如果未能解决你的问题,请参考以下文章

LibreOffice SDK:如何在 mac 中安装和配置 LibreOffice SDK

为啥在 LibreOffice Calc 中未正确检测到单元格增量

关于ubuntu中libreoffice的问题 如图,文件明明存在为啥说不存在(从windows

在Linux下下载了libreoffice,想用命令将doc转化成pdf,但是不知道为啥网上的方法都不行,求详细解答

为啥当从 word doc 或 docx 导入 o​​dt 文件时,LibreOffice 在方程中创建反问号

为啥 LibreOffice Impress 在每次按键时显示两个项目,而不是每个项目一个?