Apache Batik Transcoder inside Docker Container Blocking

断了今生、忘了曾经 提交于 2019-12-07 12:46:29

问题


We're running a Spring application within a docker container. Our application can take SVG files and transform them into PDF format to be embedded within a PDF.

The application works correctly on osx and transcodes as expected. However when run from inside a docker container, which has a different file system, the transcoder gets stuck and thrashes the cpu in some bizarre recursive file searching loop.

java.lang.Thread.State: RUNNABLE
    at java.io.UnixFileSystem.getBooleanAttributes0(Native Method)
    at java.io.UnixFileSystem.getBooleanAttributes(UnixFileSystem.java:242)
    at java.io.File.isFile(File.java:882)
    at org.apache.commons.io.filefilter.FileFileFilter.accept(FileFileFilter.java:59)
    at org.apache.commons.io.filefilter.AndFileFilter.accept(AndFileFilter.java:122)
    at org.apache.commons.io.filefilter.AndFileFilter.accept(AndFileFilter.java:122)
    at org.apache.commons.io.filefilter.OrFileFilter.accept(OrFileFilter.java:118)
    at java.io.File.listFiles(File.java:1291)
    at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:357)
    at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
    at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
    at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
    at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
    at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
    at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
    at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
    at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
    at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
    at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
    at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364

Here's a look at the stack trace of a thread that ran the PDFTranscoder. Walk is called recursively for a while and then eventually getBooleanAttributes0 is called and everything blocks.

After some further research, we found out we could take a closer look at what is happening with the strace command and saw that the system is essentially spamming the following in an endless loop.

stat("/./sys/devices/pci0000:00/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/pcspkr/input/input1/subsystem/input0/subsystem/input0/uniq", {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0 <0.000224>

We seem to be getting blocked or hanging in the stat call. But we've delved so deep into system calls now that it's proving hard to debug. Does anyone have any ideas?


回答1:


I was getting the same error. After trying many things to fix it, I came to the conclusion that it's an issue with your having fonts available to you on Mac OS X, while your (headless) Docker container OS has no fonts. The transcoder is not failing gracefully while searching for fonts all over the place. I solved it by forcing the transcoder to use the default fonts (and to not automatically look for other fonts) like this:

...
PDFTranscoder transcoder = new PDFTranscoder();
transcoder.addTranscodingHint(PDFTranscoder.KEY_AUTO_FONTS, false);
...
transcoder.transcode(transcoderInput, transcoderOutput);
...

Note this has the downside, of course, of falling back to its known fonts when it encounters one outside of the 14 fonts. I tried things to fix that but so far no luck.

I hope this helps someone.




回答2:


I had the same issues and solved it in my case. This thread helped a lot. Now I'd like to put all the parts together - maybe also for other people who come accross this.

The reason for this is the directory in which you start your Java application. I recognized that this problem occurs under the following circumstances:

  • The Java application was started in the filesystem root.
  • Auto scanning for fonts is enabled in Apache FOP.

I found a similar post in Infinite scan for fonts in Apache FOP on CentOS. The explanation of Fyodor Sherstobitov sounds plausible.

Apache FOP uses the working directory of your Java application to scan for fonts. In this case this is the filesystem root. Therefore the whole filesystem will be scanned.

The following code is copied from PDFDocumentGraphics2DConfigurator. It shows that new File(".").getAbsoluteFile().toURI() is used - which is the working directory resp. the directory in which the Java application was started.

    /**
     * Creates the {@link FontInfo} instance for the given configuration.
     * @param cfg the configuration
     * @param useComplexScriptFeatures true if complex script features enabled
     * @return the font collection
     * @throws FOPException if an error occurs while setting up the fonts
     */
    public static FontInfo createFontInfo(Configuration cfg, boolean useComplexScriptFeatures)
        throws FOPException {
        FontInfo fontInfo = new FontInfo();
        final boolean strict = false;
        if (cfg != null) {
            URI thisUri = new File(".").getAbsoluteFile().toURI();
            InternalResourceResolver resourceResolver
                    = ResourceResolverFactory.createDefaultInternalResourceResolver(thisUri);
            //TODO The following could be optimized by retaining the FontManager somewhere
            FontManager fontManager = new FontManager(resourceResolver, FontDetectorFactory.createDefault(),
                    FontCacheManagerFactory.createDefault());

            //TODO Make use of fontBaseURL, font substitution and referencing configuration
            //Requires a change to the expected configuration layout

            DefaultFontConfig.DefaultFontConfigParser parser
                    = new DefaultFontConfig.DefaultFontConfigParser();
            DefaultFontConfig fontInfoConfig = parser.parse(cfg, strict);
            DefaultFontConfigurator fontInfoConfigurator
                    = new DefaultFontConfigurator(fontManager, null, strict);
            List<EmbedFontInfo> fontInfoList = fontInfoConfigurator.configure(fontInfoConfig);
            fontManager.saveCache();
            FontSetup.setup(fontInfo, fontInfoList, resourceResolver, useComplexScriptFeatures);
        } else {
            FontSetup.setup(fontInfo, useComplexScriptFeatures);
        }
        return fontInfo;
    }

You can solve this in two ways:

  • Disable auto scanning for fonts in Apache FOP as Bob Schultz mentioned. If you do that, you will have to configure the fonts for Apache FOP manually.
  • Don't start the Java application in the filesystem root as snyman mentioned. In this case you can continue using the auto scanning for fonts.

Disable Auto Scanning

This is a snippet of the code, which configures Apache FOP with a config file. If you don't enable the auto scan in that file, you don't have to disable it programatically.

// Load configuration for manually configuring fonts
DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
Configuration cfg = cfgBuilder.build(ResourceUtil.getResourceStream("path/to/config"));

PDFTranscoder transcoder = new PDFTranscoder();
transcoder.configure(cfg);
// Disable auto scanning for fonts programatically - not necessary if you
// don't enable auto scan in your config file
// transcoder.addTranscodingHint(PDFTranscoder.KEY_AUTO_FONTS, false);

Start the Application in a Separate Folder

By specifying the WORKDIR everything happens in this folder. The auto scan runs there and finishes fast and smooth.

FROM openjdk:8-jre-alpine

WORKDIR /app

ARG JAR_FILE=target/myapp-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
...
ENTRYPOINT ["java","-jar","app.jar"]



回答3:


I had the same issue. Solved it by setting the WORKDIR variable in the DockerFile. I set it to my deployment dir, where I copy the spring jar file. Ie:

WORKDIR ${DEPLOYMENT_DIR}

using latest batik libraries in pom

<dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>batik-all</artifactId>
    <version>1.9.1</version>
</dependency>
<dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>fop</artifactId>
    <version>2.2</version>
</dependency>



回答4:


I had the same problem on my project. I solve it with the downgrade of batik to 1.7 version.

I hope this will work for you.




回答5:


Try adding the parameter '-Duser.dir=/%CATALINA_HOME/' to your CATALINA_OPTS. I encountered the same issue on my centos server.



来源:https://stackoverflow.com/questions/47664735/apache-batik-transcoder-inside-docker-container-blocking

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!