ac.simons.spring.boot.wro4j.Wro4jAutoConfiguration Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wro4j-spring-boot-starter Show documentation
Show all versions of wro4j-spring-boot-starter Show documentation
A starter for Spring Boot that provides autoconfiguration for Wro4j.
/*
* Copyright 2015-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ac.simons.spring.boot.wro4j;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import org.apache.commons.logging.LogFactory;
import ro.isdc.wro.cache.CacheKey;
import ro.isdc.wro.cache.CacheStrategy;
import ro.isdc.wro.cache.CacheValue;
import ro.isdc.wro.cache.impl.LruMemoryCacheStrategy;
import ro.isdc.wro.config.support.ConfigConstants;
import ro.isdc.wro.http.ConfigurableWroFilter;
import ro.isdc.wro.http.WroFilter;
import ro.isdc.wro.manager.factory.BaseWroManagerFactory;
import ro.isdc.wro.manager.factory.WroManagerFactory;
import ro.isdc.wro.model.factory.WroModelFactory;
import ro.isdc.wro.model.resource.processor.ResourcePostProcessor;
import ro.isdc.wro.model.resource.processor.ResourcePreProcessor;
import ro.isdc.wro.model.resource.processor.factory.ConfigurableProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.DefaultProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.ProcessorsFactory;
import ro.isdc.wro.model.resource.processor.factory.SimpleProcessorsFactory;
import ro.isdc.wro.model.resource.support.ResourceAuthorizationManager;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cache.CacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.core.log.LogAccessor;
/**
* Configures a Wro4j filter.
*
* @author Michael J. Simons
* @author Christophe Levesque
*
* @since 2015-07-11
*/
@AutoConfiguration
@ConditionalOnClass(WroFilter.class)
@ConditionalOnMissingBean(WroFilter.class)
@EnableConfigurationProperties(Wro4jProperties.class)
@AutoConfigureAfter(CacheAutoConfiguration.class)
public class Wro4jAutoConfiguration {
private static final LogAccessor LOGGER = new LogAccessor(LogFactory.getLog(Wro4jAutoConfiguration.class.getName()));
/**
* We use this to access possible processor beans inside the appplication context.
*/
private final ApplicationContext applicationContext;
/**
* Optional {@link ResourceAuthorizationManager}.
*/
private final ResourceAuthorizationManager resourceAuthorizationManager;
public Wro4jAutoConfiguration(ApplicationContext applicationContext, Optional resourceAuthorizationManager) {
this.applicationContext = applicationContext;
this.resourceAuthorizationManager = resourceAuthorizationManager.orElse(null);
}
@Bean
@ConditionalOnMissingBean(WroModelFactory.class)
WroModelFactory wroModelFactory(final Wro4jProperties wro4jProperties) {
return new ConfigurableXmlModelFactory(wro4jProperties.getModel());
}
/**
* Instantiates a {@link ProcessorsFactory} if there is no such factory
* available. If the user decided to provide Pre- or PostProcessors through
* {@link Wro4jProperties#getPreProcessors()} or
* {@link Wro4jProperties#getPostProcessors()} those are used. If either type of
* processor is configured, a {@link DefaultProcessorsFactory} will be
* returned, using the default set of processors Wro4j provides.
*
* @param wro4jProperties Configurational properties used
* @return A DefaultProcessorsFactory or a ProcessorsFactory configured to
* used the specified processors
*/
@Bean
@ConditionalOnMissingBean({WroManagerFactory.class, ProcessorsFactory.class})
ProcessorsFactory processorsFactory(final Wro4jProperties wro4jProperties) {
final List preProcessors = new ArrayList<>();
if (wro4jProperties.getPreProcessors() != null) {
for (Class extends ResourcePreProcessor> c : wro4jProperties.getPreProcessors()) {
preProcessors.add(getBeanOrInstantiateProcessor(c));
}
}
final List postProcessors = new ArrayList<>();
if (wro4jProperties.getPostProcessors() != null) {
for (Class extends ResourcePostProcessor> c : wro4jProperties.getPostProcessors()) {
postProcessors.add(getBeanOrInstantiateProcessor(c));
}
}
ProcessorsFactory rv;
if (wro4jProperties.getManagerFactory() != null) {
final Properties properties = new Properties();
if (wro4jProperties.getManagerFactory().getPreProcessors() != null) {
properties.setProperty("preProcessors", wro4jProperties.getManagerFactory().getPreProcessors());
}
if (wro4jProperties.getManagerFactory().getPostProcessors() != null) {
properties.setProperty("postProcessors", wro4jProperties.getManagerFactory().getPostProcessors());
}
rv = new ConfigurableProcessorsFactory();
((ConfigurableProcessorsFactory) rv).setProperties(properties);
}
else if (preProcessors.isEmpty() && postProcessors.isEmpty()) {
rv = new DefaultProcessorsFactory();
}
else {
rv = new SimpleProcessorsFactory();
((SimpleProcessorsFactory) rv).setResourcePreProcessors(preProcessors);
((SimpleProcessorsFactory) rv).setResourcePostProcessors(postProcessors);
}
LOGGER.debug(() -> String.format("Using ProcessorsFactory of type '%s'", rv.getClass().getName()));
return rv;
}
/**
* This method tries to load a processor from the application context by class name.
*
* If it fails, the processor is instantiated manually bot not added to the context.
*
* @param Type of the processor to load
* @param c Class of the processor to load
* @return A processor instance
*/
T getBeanOrInstantiateProcessor(final Class extends T> c) {
try {
return this.applicationContext.getBean(c);
}
catch (NoSuchBeanDefinitionException e) {
LOGGER.warn("Could not get processor from context, instantiating new instance instead");
@SuppressWarnings("unchecked")
T rv = (T) new BeanWrapperImpl(c).getWrappedInstance();
return rv;
}
}
/**
* This cache strategy will be configured if there's not already a cache
* strategy, a {@link CacheManager} is present and the name of the cache to
* use is configured.
*
* @param Type of the cache keys
* @param Type of the cache values
* @param cacheManager The cache manager to use
* @param wro4jProperties The properties (needed for the cache name)
* @return The Spring backed cache strategy
*/
@Bean
@ConditionalOnBean(CacheManager.class)
@ConditionalOnProperty("wro4j.cacheName")
@ConditionalOnMissingBean(CacheStrategy.class)
@Order(-100)
CacheStrategy springCacheStrategy(CacheManager cacheManager, Wro4jProperties wro4jProperties) {
LOGGER.debug("Creating cache strategy 'SpringCacheStrategy'");
return new SpringCacheStrategy<>(cacheManager, wro4jProperties.getCacheName());
}
/**
* This is the default "Least recently used memory cache" strategy of Wro4j
* which will be configured per default.
*
* @param Type of the cache keys
* @param Type of the cache values
* @return A default Wro4j cache strategy
*/
@Bean
@ConditionalOnMissingBean(CacheStrategy.class)
@Order(-90)
CacheStrategy defaultCacheStrategy() {
LOGGER.debug("Creating cache strategy 'LruMemoryCacheStrategy'");
return new LruMemoryCacheStrategy<>();
}
/**
* Builds the {@link WroManagerFactory} used for the Wro4j filter to be
* created if no WroManagerFactory is already registered.
*
* @param wroModelFactory THe model factory to use for the manager factory
* @param processorsFactory The processors factory to use for the manager
* @param cacheStrategy The cache strategy to use
*
* @return A new WroManagerFactory
*/
@Bean
@ConditionalOnMissingBean(WroManagerFactory.class)
WroManagerFactory wroManagerFactory(
final WroModelFactory wroModelFactory,
final ProcessorsFactory processorsFactory,
final CacheStrategy cacheStrategy) {
return new BaseWroManagerFactory()
.setModelFactory(wroModelFactory)
.setProcessorsFactory(processorsFactory)
.setCacheStrategy(cacheStrategy)
.setResourceAuthorizationManager(this.resourceAuthorizationManager);
}
/**
* The final step in configuring the Wro4j filter based on the existing or
* previously configured {@code WroManagerFactory} and the additional
* properties.
*
* @param wroManagerFactory An existing or the newly configured manager
* @param wro4jProperties The properties used to setup this starter
*
* @return A servlet filter which later is registered through Spring means
*/
@Bean
ConfigurableWroFilter wroFilter(WroManagerFactory wroManagerFactory, Wro4jProperties wro4jProperties) {
ConfigurableWroFilter wroFilter = new ConfigurableWroFilter();
wroFilter.setProperties(wroFilterProperties(wro4jProperties));
wroFilter.setWroManagerFactory(wroManagerFactory);
return wroFilter;
}
@SuppressWarnings({"squid:MethodCyclomaticComplexity"})
Properties wroFilterProperties(Wro4jProperties wro4jProperties) {
final Properties properties = new Properties();
properties.setProperty(ConfigConstants.debug.name(), String.valueOf(wro4jProperties.isDebug()));
properties.setProperty(ConfigConstants.minimizeEnabled.name(), String.valueOf(wro4jProperties.isMinimizeEnabled()));
properties.setProperty(ConfigConstants.gzipResources.name(), String.valueOf(wro4jProperties.isGzipResources()));
if (wro4jProperties.getResourceWatcherUpdatePeriod() != null) {
properties.setProperty(ConfigConstants.resourceWatcherUpdatePeriod.name(), String.valueOf(wro4jProperties.getResourceWatcherUpdatePeriod()));
}
properties.setProperty(ConfigConstants.resourceWatcherAsync.name(), String.valueOf(wro4jProperties.isResourceWatcherAsync()));
if (wro4jProperties.getCacheUpdatePeriod() != null) {
properties.setProperty(ConfigConstants.cacheUpdatePeriod.name(), String.valueOf(wro4jProperties.getCacheUpdatePeriod()));
}
if (wro4jProperties.getModelUpdatePeriod() != null) {
properties.setProperty(ConfigConstants.modelUpdatePeriod.name(), String.valueOf(wro4jProperties.getModelUpdatePeriod()));
}
if (!(wro4jProperties.getHeader() == null || wro4jProperties.getHeader().trim().isEmpty())) {
properties.setProperty(ConfigConstants.header.name(), wro4jProperties.getHeader());
}
properties.setProperty(ConfigConstants.parallelPreprocessing.name(), String.valueOf(wro4jProperties.isParallelPreprocessing()));
if (wro4jProperties.getConnectionTimeout() != null) {
properties.setProperty(ConfigConstants.connectionTimeout.name(), String.valueOf(wro4jProperties.getConnectionTimeout()));
}
if (!(wro4jProperties.getEncoding() == null || wro4jProperties.getEncoding().trim().isEmpty())) {
properties.setProperty(ConfigConstants.encoding.name(), wro4jProperties.getEncoding());
}
properties.setProperty(ConfigConstants.ignoreMissingResources.name(), String.valueOf(wro4jProperties.isIgnoreMissingResources()));
properties.setProperty(ConfigConstants.ignoreEmptyGroup.name(), String.valueOf(wro4jProperties.isIgnoreEmptyGroup()));
properties.setProperty(ConfigConstants.ignoreFailingProcessor.name(), String.valueOf(wro4jProperties.isIgnoreFailingProcessor()));
properties.setProperty(ConfigConstants.cacheGzippedContent.name(), String.valueOf(wro4jProperties.isCacheGzippedContent()));
properties.setProperty(ConfigConstants.jmxEnabled.name(), String.valueOf(wro4jProperties.isJmxEnabled()));
if (!(wro4jProperties.getMbeanName() == null || wro4jProperties.getMbeanName().trim().isEmpty())) {
properties.setProperty(ConfigConstants.mbeanName.name(), wro4jProperties.getMbeanName());
}
return properties;
}
/**
* Registers the {@code wroFilter} through a Spring
* {@link FilterRegistrationBean}.
*
* @param wroFilter The configured {@code wroFilter}
* @param wro4jProperties Needed for the url pattern to which the filter
* should be registered
*
* @return The Spring {@code FilterRegistrationBean}
*/
@Bean
FilterRegistrationBean wro4jFilterRegistration(ConfigurableWroFilter wroFilter, Wro4jProperties wro4jProperties) {
final FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(wroFilter);
filterRegistrationBean.addUrlPatterns(wro4jProperties.getFilterUrl() + "/*");
return filterRegistrationBean;
}
}