1

I've successfully implemented a custom post processor filter with the help of the wro4j documentation.

Its job is to generate and prepend SASS vars to a group of SASS files which are then handed off to the rubySassCss filter for transpiling, and it's doing this job well.

The problem is that I wanted to hand the job of determining the SASS vars off to a custom ThemeManager @Service managed by Spring. I hadn't considered that the filter wouldn't be able to see the autowired @Service but that seems to be the case.

When I @Autowire the @Service into a controller, it works fine, but when I try the same thing with the filter I get a NPE when attempting to use it.

Is there a way to make the @Service visible to the filters or am I approaching this the wrong way?

Thanks for any help.

UPDATE:

It's taken some doing and attacking from a lot of angles, but I seem to be having success with autowiring my themeManagerService into the app configuration where I have my WRO filterRegistrationBean bean. I then pass the themeManagerService bean as a second argument to my custom ConfigurableWroManagerFactory.

Living in the custom WroManagerFactory is a reference to a custom UriLocator, which takes that themeManagerService as an argument. The custom UriLocator is invoked by a CSS resource containing an arbitrary keyword within a group.

The new UriLocator is able to generate a ByteArrayInputStream from what the themeManagerService provides it and pass it into the pipeline.

Simple.

I'll follow up when this approach pans/fizzles out.

1 Answers1

1

In the end, I was able to provide the spring managed ThemeManagerService directly to the custom post processor, rather than relying on a custom UriLocator. I had tried that early on, but forgot to call super() in the new constructor, so the processor registration system was breaking.

I pass the @Autowired ThemeManagerService to my CustomConfigurableWroManagerFactory when registering the WRO bean:

@Autowired
ThemeManagerService themeManagerService;

@Bean
FilterRegistrationBean webResourceOptimizer(Environment env) {
    FilterRegistrationBean fr = new FilterRegistrationBean();
    ConfigurableWroFilter filter = new ConfigurableWroFilter();
    Properties props = buildWroProperties(env);
    filter.setProperties(props);
    //The overridden constructor passes ThemeManager along
    filter.setWroManagerFactory(new CustomConfigurableWroManagerFactory(props,themeManagerService));
    filter.setProperties(props);
    fr.setFilter(filter);
    fr.addUrlPatterns("/wro/*");
    return fr;
}

The constructor injection of ThemeManagerService into CustomConfigurableWroManagerFactory means it can be passed along to the custom postprocessor as it's registered by contributePostProcessors:

public class CustomConfigurableWroManagerFactory extends Wro4jCustomXmlModelManagerFactory {
    private ThemeManagerService themeManagerService;

    public CustomConfigurableWroManagerFactory(Properties props,ThemeManagerService themeManagerService) {
        //forgetting to call super derailed me early on
        super(props);
        this.themeManagerService = themeManagerService;
    }

    @Override
    protected void contributePostProcessors(Map<String, ResourcePostProcessor> map) {
        //ThemeManagerService is provided as the custom processor is registered
        map.put("repoPostProcessor", new RepoPostProcessor(themeManagerService));
    }
}

Now, the post processor has access to ThemeManagerService:

@SupportedResourceType(ResourceType.CSS)
public class RepoPostProcessor implements ResourcePostProcessor {
    private ThemeManagerService themeManagerService;

    public RepoPostProcessor(ThemeManagerService themeManagerService) {
        super();
        this.themeManagerService = themeManagerService;
    }

    public void process(final Reader reader, final Writer writer) throws IOException {
        String resourceText = "/* The custom PostProcessor fetched the following SASS vars from the ThemeManagerService: */\n\n"; 
        resourceText += themeManagerService.getFormattedProperties();
        writer.append(resourceText);
        //read in the merged SCSS and add it after the custom content 
        writer.append(IOUtils.toString(reader));
        reader.close();
        writer.close();
    }  
}

This approach is working as expected/intended so far. Hope it comes in handy for someone else.

Wro4j is a great tool and much appreciated.