Merge configuration is handled through your web.xml file. The merge facility in Broadleaf primarily operates by intelligently mixing one or more Spring application context files. The final merged version of the application context is then passed to Spring for processing.
There are two different types of applicationContext files that exist in a typical Broadleaf application: core Spring files listed in the
patchConfigLocation context param in web.xml, and Spring MVC files listed in the
contextConfigLocation servlet init param in web.xml. The merge behavior of these two types of files is slightly different.
Broadleaf is capable of intelligently merging beans by providing a specialized
MergeContextLoader, which is invoked as a listener by the following configuration in web.xml:
<listener> <listener-class>org.broadleafcommerce.common.web.extensibility.MergeContextLoaderListener</listener-class> </listener>
This ContextLoader will analyze the contents of all applicationContexts specified in the
patchConfigLocation and do its best to merge properties using two strategies.
This is the newer of the two strategies used in Broadleaf, and all new configurations are written in this pattern. The easiest way to show this strategy is by example. Let's take a look at some of the configuration that lives in a Broadleaf applicationContext file:
<bean id="blDialectProcessors" class="org.springframework.beans.factory.config.SetFactoryBean"> <property name="sourceSet"> <set> <ref bean="blAddSortLinkProcessor" /> <ref bean="blCategoriesProcessor" /> ... other bean references ... </set> </property> </bean> <bean id="blDialect" class="org.broadleafcommerce.common.web.dialect.BLCDialect"> <property name="processors" ref="blDialectProcessors" /> </bean>
What we have here is a bean called
blDialect that has a property in it called
processors which takes a Set of beans that implement the Thymeleaf
IProcessor. This applicationContext file also defines another bean called
blDialectProcessors of type
SetFactoryBean. This means that Spring will do some processing for this SetFactoryBean, and when it is injected into the
processors property, it will be transformed into a Set.
We are able to contribute an additional processor to this Set without copy-pasting the entire
blDialectProcessors bean. We do so with the following configuration:
<bean id="blDialectAdditionalProcessors" class="org.springframework.beans.factory.config.SetFactoryBean"> <property name="sourceSet"> <set> <ref bean="blAdditionalProcessorOne"/> <ref bean="blAdditionalProcessorTwo"/> </set> </property> </bean> <bean class="org.broadleafcommerce.common.extensibility.context.merge.LateStageMergeBeanPostProcessor"> <property name="collectionRef" value="blDialectAdditionalProcessors" /> <property name="targetRef" value="blDialectProcessors" /> </bean>
Firstly, this additional configuration triggers the creation of a new bean,
blDialectAdditionalProcessors that then defines two extra processors that we want to add to the
blDialect processors property. Secondly, we define a
LateStageMergeBeanPostProcessor that will merge our new bean into the existing bean. Lastly, when the existing bean is injected into
blDialect, it will contain our extra entries.
This strategy is applicable for adding entries into any pre-defined Broadleaf beans.
This is the older merge strategy and is still used in parts of the system that have not been migrated to the new style. It leverages various XPath based merge strategies defined in default.properties. This approach actually modifies the XML structure of the merged applicationContexts before it reaches Spring. Let's take a look at an example of registering multiple dialects in the
<bean id="blWebTemplateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine"> <property name="dialects"> <set> <ref bean="thymeleafSpringStandardDialect" /> <ref bean="blDialect" /> </set> </property> </bean>
If we wanted to add our own custom dialect, we could simply place the following bean definition in our applicationContext file:
<bean id="blWebTemplateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine"> <property name="dialects"> <set> <ref bean="myCustomDialect" /> </set> </property> </bean>
Because of this chunk in default.properties:
handler.14=org.broadleafcommerce.common.extensibility.context.merge.handlers.NodeReplaceInsert priority.14=14 xpath.14=/beans/bean[@id='blWebTemplateEngine']/* handler.14.1=org.broadleafcommerce.common.extensibility.context.merge.handlers.InsertItems priority.14.1=1 xpath.14.1=/beans/bean[@id='blWebTemplateEngine']/property[@name='dialects']/set/ref
the XML that is produced would be:
<bean id="blWebTemplateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine"> <property name="dialects"> <set> <ref bean="thymeleafSpringStandardDialect" /> <ref bean="blDialect" /> <ref bean="myCustomDialect" /> </set> </property> </bean>
When possible, you want to use Strategy 1. Whenever the goal is to merge into an existing
MapFactoryBean, this is the right approach.
If you want to contribute an entry to a collection that is not handled by one of those three classes, the next step is to check if an entry in
default.properties exists. If it does, you are able to use the second merge strategy.
On occassion, you may need to override the Broadleaf merging process.
This can be done by adding a file named
broadleaf-commerce/skipMergeComponents.txt to your classpath. For example, in the DemoSite, put this file in the
The file should contain a list of component names for which you do not want the Broadleaf Commerce merge process to be used. The example below would not perform merging on the
By adding a component to this list, the default Spring merging process will be used.
For servlet-level application context files, Strategy 2 described above is not applicable, as there is no special ContextLoader to process the XML. However, the first strategy is still applicable for any beans that utilize the new FactoryBean approach.