upgradeapache-fop

Apache FOP: upgrading from 1.1 to 2.1


I am following the migration guide, but I don't seem to get it right.

In FOP 1.1 I have this working code:

public class XsltFactory {
    private static final String FO_CONFIG_FILE = "/path/to/fop-config.xml";

    private static FopFactory fopFactory;

    private static synchronized void initFopFactory(final ServletContext context) throws Exception {
        Configuration cfg = new DefaultConfigurationBuilder().build(XsltFactory.class.getResourceAsStream(FO_CONFIG_FILE));
        fopFactory = FopFactory.newInstance();
        fopFactory.setURIResolver(new ServletContextURIResolver(context));
        fopFactory.setUserConfig(cfg);
    }
}

I adapted the above code to stick with FOP 2.1:

public class XsltFactory {
    private static final String FO_CONFIG_FILE = "/path/to/fop-config.xml";

    private static FopFactory fopFactory;

    private static synchronized void initFopFactory(final ServletContext context) throws Exception {
        Configuration cfg = new DefaultConfigurationBuilder().build(XsltFactory.class.getResourceAsStream(FO_CONFIG_FILE));

        FopFactoryBuilder fopFactoryBuilder = new FopFactoryBuilder(
            new URI(ServletContextURIResolver.SERVLET_CONTEXT_PROTOCOL),
            new URIResolverAdapter(new ServletContextURIResolver(context))
        );

        fopFactoryBuilder.setConfiguration(cfg);
        fopFactory = fopFactoryBuilder.build();
    }
}

But I get the following error:

java.lang.Exception: Fail to create PDF
    at ....web.controller.PrintPdfController.renderPdf(PrintPdfController.java:181)
    [...]
    at weblogic.work.ExecuteThread.run(ExecuteThread.java:263)
Caused by: java.net.URISyntaxException: Expected scheme-specific part at index 16: servlet-context:
    at java.net.URI$Parser.fail(URI.java:2829)
    at java.net.URI$Parser.failExpecting(URI.java:2835)
    at java.net.URI$Parser.parse(URI.java:3038)
    at java.net.URI.<init>(URI.java:595)
    [...]
    ... 42 common frames omitted

The PDF fails to load, since it failed at being created.

EDIT:

After adding + "///" after SERVLET_CONTEXT_PROTOCOL the context, I now get:

Caused by: java.net.MalformedURLException: unknown protocol: servlet-context
    at java.net.URL.<init>(URL.java:592)
    at java.net.URL.<init>(URL.java:482)
    at java.net.URL.<init>(URL.java:431)
    at java.net.URI.toURL(URI.java:1096)
    at org.apache.fop.fonts.FontDetectorFactory$DefaultFontDetector.detect(FontDetectorFactory.java:94)
    ... 59 common frames omitted

Solution

  • After a few days of investigation, the migration has finally been done successfully. The problem was coming from the URI resolver, and fixing this problem created new problems, which I solved subsequently.

    The guide at https://xmlgraphics.apache.org/fop/2.1/upgrading.html is of relatively limited help.

    The core of the problem is the URI resolver. You now have to define a custom resolver, but NOT as in the example provided at: https://xmlgraphics.apache.org/fop/2.0/servlets.html

    ResourceResolver resolver = new ResourceResolver() {
     public OutputStream getOutputStream(URI uri) throws IOException {
      URL url = getServletContext().getResource(uri.toASCIIString());
      return url.openConnection().getOutputStream();
     }
    
     public Resource getResource(URI uri) throws IOException {
      return new Resource(getServletContext().getResourceAsStream(uri.toASCIIString()));
     }
    };
    

    The right way of doing it is:

    ResourceResolver resolver = new ResourceResolver() {
     public OutputStream getOutputStream(URI uri) throws IOException {
      URL url = context.getResource(uri.getPath());
      return url.openConnection().getOutputStream();
     }
     public Resource getResource(URI uri) throws IOException {
      return new Resource(context.getResourceAsStream(uri.getPath()));
     }
    };
    

    Instead of uri.toASCIIString(), the correct syntax is uri.getPath().

    In addition, we had to remove all "servlet-context:" markup in fonts URIs (in fop-config.xml) and images URIs (in any XSL transformation file or template).

    Finally, I got an issue with hyphenation: FOP could not find .hyp files anymore, because for some reason, the baseUri was being used instead of the custom context resolver (I had to dig into FOP's source files to find out). So, I had to modify the getResource method of my custom resolver. I know this is a hack, but it works and it is sufficient for me as I already spent three days on this problem):

    public OutputStream getOutputStream(URI uri) throws IOException {
      URL url = context.getResource(uri.getPath());
      return url.openConnection().getOutputStream();
     }
     public Resource getResource(URI uri) throws IOException {
      InputStream stream = null;
      /*
       * For some reason, in FOP 2.x, the hyphenator does not use the
       * classpath fop-hyph.jar.
       * 
       * This causes trouble as FOP tries to find "none.hyp" in the
       * war directory. Setting
       * <hyphenation-base>/WEB-INF/hyph</hyphenation-base> in the
       * fop-config.xml file does not solve the issue. The only
       * solution I could find is to programmatically detect when a
       * .hyp file is trying to be loaded. When this occurs, I modify
       * the path so that the resolver gets the right resource.
       * 
       * This is a hack, but after spending three days on it, I just
       * went straight to the point and got a workaround.
       */
      if (uri.getPath().endsWith('.hyp')) {
       String relUri = uri.getPath().substring(uri.getPath().indexOf(baseUri.getPath()) + baseUri.getPath().length());
       stream = context.getResourceAsStream(FopManager.HYPH_DIR + relUri);
      } else {
       stream = context.getResourceAsStream(uri.getPath());
      }
      Resource res = new Resource(stream);
      return res;
     }
    };
    

    Note that I also had to create the none.hyp file manually, since it does not exist in the .hyp files provided by OFFO. I just copied en.hyp and renamed it none.hyp. This solved my last problem.

    I hope this saves someone a few days of work ;)