Skip to content

Rendering pre process Mark Factories Hint

Jody Garnett edited this page Mar 29, 2022 · 4 revisions

Description

Motivation

We found on some deployments GeoServer WKT evaluation starts with SVG instead GeoTools core ones.

Tomcat 8+ class loading order changed

From https://www.xspdf.com/resolution/50039947.html Order of loading jar files from lib directory, Like many of the issues that trouble new Tomcat users, this problem is usually quite easy WEB-INF/classes and WEB-INF/lib, respectively and in that order. In previous versions of Tomcat, the classloader hierarchy worked a little differently. A classloader for Apache Tomcat 8 which loads the jars of WEB-INF lib in alphabetical order. Prior to version 8, Apache Tomcat loaded the jars of the WEB-INF lib directory in alphabetical order. Starting with version 8, the order is not predictable anymore and can lead to erratic behaviors, especially when deploying applications in a clustered environment.

Due to tomcat 8+ random class loading order, on some environments we have for every feature the SVGMarkFactory is tried to execute before the Geotools core ones: SVGMarkFactory.getShape(Graphics2D, Expression, Feature)

Take into account the issue randomly happens by tomcat installation/deployment basis. That's why sometimes it happens after a redeployment.

The SVG check is slow.

We did a performance improvement before for URL converter code, but anyway SVG WKT parsing check and exception handling is expensive for lots of features in the rendering phase. We don't have, currently, a way to handle the factories order or execution allow rules in GeoServer. That's why we are proposing this configuration enhacement.

Description

GeoTools Rendering Hints processor.

Make Geotools rendering for Mark factories search for an optional MarkFactoryProcessorProvider instance and use it to pre-process MarkFactory iterator obtained from the DynamicSymbolFactoryFinder.getMarkFactories() method in the code line where the Mark Factories are loaded.

The MarkFactoryProcessorProvider interface will be coded in this example:

public interface MarkFactoryProcessorProvider {

    public static final Hints.Key MARK_FACTORY_PROCESSOR_PROVIDER_KEY =
            new Hints.Key(MarkFactoryProcessorProvider.class);

    /**
     * Return the {@link MarkFactoryProcessor} corresponding to the provided feature.
     *
     * @param feature the feature to process or null if not needed.
     * @return the {@link MarkFactoryProcessor} instance.
     */
    MarkFactoryProcessor get(Feature feature);
}

The MarkFactoryProcessor interface:

interface MarkFactoryProcessor {
    /** Used to filter and sort available factories */
    Iterator<MarkFactory> process( Iterator<MarkFactory> factories );
}

Applications will be able to implement and pass this new interface instances on the rendering hints to pre-process ordering and filtering the Mark Factories to allow better rendering performance.

Alternate Proposal

Alternate proposal is to update DynamicSymbolFactoryFinder with the missing getMarkFactories( Hints ) method (one already existed for getExternalGraphicFactories(Hints) and provide hints for both sorting and filtering.

class DynamicSymbolFactoryFinder {
  
  public static final Hints.Key MARK_FACTORY_ORDER = new Hints.Key(Comparator.class);
  public static final Hints.Key MARK_FACTORY_FILTER = new Hints.Key(Predicate.class);

  Iterator<MarkFactory> getMarkFactories()
  Iterator<MarkFactory> getMarkFactories( Hints )
  Iterator<ExternalGraphicFactory> getExternalGraphicFactories()
  Iterator<ExternalGraphicFactory> getExternalGraphicFactories(Hints hints)
}

The SLDStyleFactory change is minimal. The getIcon(ExternalGraphic eg, Object feature, double size) method already shows how to use hints to control factory lookup:

        // scan the external graphic factories and see which one can be used
        Iterator<ExternalGraphicFactory> it =
                DynamicSymbolFactoryFinder.getExternalGraphicFactories(new Hints(renderingHints));

Applying the same approach to getShape(Mark mark, Object feature):

    private Shape getShape(Mark mark, Object feature) {
        ....   
        Iterator<MarkFactory> it = DynamicSymbolFactoryFinder.getMarkFactories(new Hints(renderingHints));
        while (it.hasNext()) {
            MarkFactory factory = it.next();
            ...
        }
        ... 
    }

For this to work DynamicSymbolFactoryFinder must respect the provided hints:

    public static synchronized Iterator<MarkFactory> getMarkFactories() {
        return getServiceRegistry().getFactories(MarkFactory.class, null, null).iterator();
    }
    public static synchronized Iterator<MarkFactory> getMarkFactories( Hints hints ) {
        Comparator sort = hints != null ? hints.get(MARK_FACTORY_ORDER) : null;
        Predicate filter = hints != null ? hints.get(MARK_FACTORY_FILTER) : null;

        getServiceRegistry().getFactories(MarkFactory.class, filter, hints);
        
        return sort != null ? factories.sorted(sort).iterator() : factories:iterator();
    }

If a desired a method can be provided for global ordering (but not filtering):

    public static synchronized void setOrdering(final Class<T> category, final Comparator<T> comparator)
        getServiceRegistry().setOrdering( category, comparator );
    }

This alternate proposal has the benefit of keeping factory lookup code together and does not introduce new api concepts to the library.

Assigned to Release

GeoTools 25.3, 26.0

Status

Choose one of:

  • Under Discussion
  • In Progress
  • Completed
  • Rejected,
  • Deferred

Voting:

  • Andrea Aime:
  • Ian Turton:
  • Jody Garnett: +1 Alternate
  • Nuno Oliveira:
  • Simone Giannecchini:
  • Torben Barsballe:

Tasks

Proposal tasks:

#. Implement MarkFactoryProcessorProvider and MarkFactoryProcessor interfaces, including Hints. #. Test case to ensure Hint is respected by SLDStyleFactory #. Optional: Provide a default implementation that sorts the slower factories (such as SVGMarkFactory to the end)

Clone this wiki locally