Spring Framework
  1. Spring Framework
  2. SPR-6523

Introduce 'merge' attribute for util namespace collection elements

    Details

    • Type: Improvement Improvement
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: None
    • Fix Version/s: General Backlog
    • Component/s: Core
    • Labels:
      None
    • Last commented by a User:
      true

      Description

      Section C.2.2.4 <util:list/> of the reference manual states:

      Finally, you can also control the merging behavior using the 'merge' attribute of the <util:list/> element; collection merging is described in more detail in the section called "Collection merging".

      This is, however, not true. In contrast to the standard support for collections (i.e., array, list, set, map) in the beans namespace, the util namespace does not support the merge attribute for collections.

      Is this an oversight in the documentation or missing functionality in the util namespace support?

      On a related note (perhaps worthy of its own JIRA issue), given the following application context configuration and JUnit test class...

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:util="http://www.springframework.org/schema/util"
        xsi:schemaLocation="
          http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
          http://www.springframework.org/schema/util
          http://www.springframework.org/schema/util/spring-util-3.0.xsd">
      
        <bean id="listHolderParent" class="com.example.MergedListTests$ListHolder"
          abstract="true">
          <property name="list">
            <list>
              <value>red</value>
              <value>green</value>
              <value>blue</value>
            </list>
          </property>
        </bean>
      
        <bean id="listHolder" parent="listHolderParent">
          <property name="list">
            <list merge="true">
              <value>cyan</value>
              <value>magenta</value>
              <value>yellow</value>
              <value>black</value>
            </list>
          </property>
        </bean>
      
      </beans>
      
      package com.example;
      
      import static org.junit.Assert.assertEquals;
      import static org.junit.Assert.assertNotNull;
      
      import java.util.List;
      
      import org.junit.Test;
      import org.springframework.context.ApplicationContext;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      
      public class MergedListTests {
      
        @Test
        public void mergedList() {
          ApplicationContext appCtx = new ClassPathXmlApplicationContext(
            "/com/example/MergedListTests-context.xml");
          ListHolder listHolder = appCtx.getBean("listHolder", ListHolder.class);
          assertNotNull(listHolder);
          assertEquals(7, listHolder.getList().size());
        }
      
        public static class ListHolder {
      
          private List<String> list;
      
          public List<String> getList() {
            return this.list;
          }
      
          public void setList(List<String> list) {
            this.list = list;
          }
        }
      }
      

      ... changing the <list> definition in listHolderParent to <util:list> results in the following exception:

      java.lang.IllegalArgumentException: Cannot merge with object of type [class org.springframework.beans.factory.config.BeanDefinitionHolder]
      at org.springframework.beans.factory.support.ManagedList.merge(ManagedList.java:98)
      at org.springframework.beans.factory.support.ManagedList.merge(ManagedList.java:1)
      at org.springframework.beans.MutablePropertyValues.mergeIfRequired(MutablePropertyValues.java:221)
      at org.springframework.beans.MutablePropertyValues.addPropertyValue(MutablePropertyValues.java:169)
      at org.springframework.beans.MutablePropertyValues.addPropertyValues(MutablePropertyValues.java:138)
      at org.springframework.beans.factory.support.AbstractBeanDefinition.overrideFrom(AbstractBeanDefinition.java:300)
      at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedBeanDefinition(AbstractBeanFactory.java:1114)
      at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedBeanDefinition(AbstractBeanFactory.java:1054)
      at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1040)
      at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanNamesForType(DefaultListableBeanFactory.java:294)
      at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:578)
      at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:398)
      at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
      at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
      at com.example.MergedListTests.mergedList(MergedListTests.java:17)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      ...

        Issue Links

          Activity

          Hide
          Stevo Slavić added a comment -

          Maybe something similar to properties-ref of context:property-placeholder can be supported for collections and properties in util namespace.

          With this, one could define:

          <util:properties id="someProperties" properties-ref="someOtherProperties">
            ...
          </util:properties>
          
          <util:list id="someList" collection-ref="someOtherList">
            ...
          </util:list>
          
          <util:list id="someOtherList" collection-ref="someSet">
            ...
          </util:list>
          
          <util:set id="someSet" collection-ref="someOtherSet" ignore-colissions="false">
            ...
          </util:set>
          
          <util:set id="someOtherSet" collection-ref="yetAnotherList" ignore-colissions="true">
            ...
          </util:set>
          
          <util:list id="yetAnotherList">
            ...
          </util:list>
          
          <util:map id="someMap" map-ref="someOtherMap" ignore-colissions="false">
            ...
          </util:map>
          
          <util:map id="someOtherMap">
            ...
          </util:map>
          

          ignore-colissions can default to false with a meaning that merge should fail if destination already contains elements/entries from source. If set to true spring should ignore/skip this check, or optionally log a info/warning when colission occurs.

          For properties it's a bit tricky, since it already supports additional properties to be provided via location attribute, it would be good if both location and properties-ref can be used.

          I prefer -ref over merge convention requested in SPR-5199 as I find it confusing to have different beans with same id in same context, and id would have to become not unique. Behavior somewhat equivavalent to merge can be achieved with profiles support already added in spring 3.1 M1 with -ref attribute support - one could define a single common base bean which is always active, and for different profiles/environments activate a different bean, with same id, like in following example:

          <beans xmlns="http://www.springframework.org/schema/beans"
          	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          	xmlns:p="http://www.springframework.org/schema/p"
          	xmlns:util="http://www.springframework.org/schema/util"
          	xsi:schemaLocation="
          		http://www.springframework.org/schema/beans
          		http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
          		http://www.springframework.org/schema/util
          		http://www.springframework.org/schema/util/spring-util-3.1.xsd">
          
            <util:properties id="quartzCommonProperties"
                  location="classpath:foo/bar/cfg/quartz-common.properties" />
          
            <bean id="clusteredQuartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
                p:quartzProperties-ref="quartzProperties" />
          
            <beans profile="production">
                <util:properties id="quartzProperties" properties-ref="quartzCommonProperties"
                      location="classpath:foo/bar/cfg/quartz-production.properties" />
            </beans>
          
            <beans profile="development">
                <util:properties id="quartzProperties" properties-ref="quartzCommonProperties"
                      location="classpath:foo/bar/cfg/quartz-development.properties" />
            </beans>
          </beans>
          
          Show
          Stevo Slavić added a comment - Maybe something similar to properties-ref of context:property-placeholder can be supported for collections and properties in util namespace. With this, one could define: <util:properties id= "someProperties" properties-ref= "someOtherProperties" > ... </util:properties> <util:list id= "someList" collection-ref= "someOtherList" > ... </util:list> <util:list id= "someOtherList" collection-ref= "someSet" > ... </util:list> <util:set id= "someSet" collection-ref= "someOtherSet" ignore-colissions= " false " > ... </util:set> <util:set id= "someOtherSet" collection-ref= "yetAnotherList" ignore-colissions= " true " > ... </util:set> <util:list id= "yetAnotherList" > ... </util:list> <util:map id= "someMap" map-ref= "someOtherMap" ignore-colissions= " false " > ... </util:map> <util:map id= "someOtherMap" > ... </util:map> ignore-colissions can default to false with a meaning that merge should fail if destination already contains elements/entries from source. If set to true spring should ignore/skip this check, or optionally log a info/warning when colission occurs. For properties it's a bit tricky, since it already supports additional properties to be provided via location attribute, it would be good if both location and properties-ref can be used. I prefer -ref over merge convention requested in SPR-5199 as I find it confusing to have different beans with same id in same context, and id would have to become not unique. Behavior somewhat equivavalent to merge can be achieved with profiles support already added in spring 3.1 M1 with -ref attribute support - one could define a single common base bean which is always active, and for different profiles/environments activate a different bean, with same id, like in following example: <beans xmlns= "http: //www.springframework.org/schema/beans" xmlns:xsi= "http: //www.w3.org/2001/XMLSchema-instance" xmlns:p= "http: //www.springframework.org/schema/p" xmlns:util= "http: //www.springframework.org/schema/util" xsi:schemaLocation=" http: //www.springframework.org/schema/beans http: //www.springframework.org/schema/beans/spring-beans-3.1.xsd http: //www.springframework.org/schema/util http: //www.springframework.org/schema/util/spring-util-3.1.xsd"> <util:properties id= "quartzCommonProperties" location= "classpath:foo/bar/cfg/quartz-common.properties" /> <bean id= "clusteredQuartzScheduler" class= "org.springframework.scheduling.quartz.SchedulerFactoryBean" p:quartzProperties-ref= "quartzProperties" /> <beans profile= "production" > <util:properties id= "quartzProperties" properties-ref= "quartzCommonProperties" location= "classpath:foo/bar/cfg/quartz-production.properties" /> </beans> <beans profile= "development" > <util:properties id= "quartzProperties" properties-ref= "quartzCommonProperties" location= "classpath:foo/bar/cfg/quartz-development.properties" /> </beans> </beans>
          Hide
          Stevo Slavić added a comment -

          This issue is related to SPR-1502 and SPR-4760

          Show
          Stevo Slavić added a comment - This issue is related to SPR-1502 and SPR-4760
          Hide
          Chris Beams added a comment -

          As Juergen originally mentioned, the lack of merge support for collections has been a limitation of the util namespace from its inception. The real 'bug' here was with documentation, which Sam has since fixed. This is really an improvement request, and now marking it as such.

          Show
          Chris Beams added a comment - As Juergen originally mentioned, the lack of merge support for collections has been a limitation of the util namespace from its inception. The real 'bug' here was with documentation, which Sam has since fixed. This is really an improvement request, and now marking it as such.
          Hide
          Eric Haszlakiewicz added a comment -

          It sounds like this also needs a "parent" attribute like the regular bean element has, so you can specify what you're merging with. i.e.

           
              <util:map id="map1">
                  ...
              </util:map>
              <util:map id="map2" parent="map1" merge="true">
                  ...
              </util:map>
          
          Show
          Eric Haszlakiewicz added a comment - It sounds like this also needs a "parent" attribute like the regular bean element has, so you can specify what you're merging with. i.e. <util:map id= "map1" > ... </util:map> <util:map id= "map2" parent= "map1" merge= "true" > ... </util:map>
          Hide
          Matthew T. Adams added a comment -

          One thing that I'd like to make sure of in this request is that the ordering be controllable, in particular with maps. I'm commenting here as my issue, SPR-8835, "Add MapCombiningFactoryBean", was resolved as a duplicate & Chris Beams just wanted to make sure that I got my $0.02 in WRT ordering. See https://jira.springsource.org/secure/attachment/19107/MapCombiningFactoryBean.java for more information.

          Show
          Matthew T. Adams added a comment - One thing that I'd like to make sure of in this request is that the ordering be controllable, in particular with maps. I'm commenting here as my issue, SPR-8835 , "Add MapCombiningFactoryBean", was resolved as a duplicate & Chris Beams just wanted to make sure that I got my $0.02 in WRT ordering. See https://jira.springsource.org/secure/attachment/19107/MapCombiningFactoryBean.java for more information.

            People

            • Assignee:
              Juergen Hoeller
              Reporter:
              Sam Brannen
              Last updater:
              Sam Brannen
            • Votes:
              13 Vote for this issue
              Watchers:
              16 Start watching this issue

              Dates

              • Created:
                Updated:
                Days since last comment:
                2 years, 1 week ago