springwro4j

Wro4j: Accessing Spring @Service from custom post processor


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.


Solution

  • 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.