Announcement Announcement Module
Collapse
No announcement yet.
<util:list> and merge attribute Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • <util:list> and merge attribute

    I try to use the new <util:list> configuration, especially with the ability to merge lists. Unfortunately, I wasn't able to get it work.

    For example, I have two lists with email addresses which should be merged. The merged list should be an attribute of another bean.
    Code:
    <util:list id="emailAdresses">
        <value>[email protected]</value>
    </util:list>
    
    <util:list id="emailAdresses" merge="true">
       <value>[email protected]</value>
       <value>someoneelse@s[email protected]</value>
    </util:list>
    
    <bean id="someBean" class="my.bean.Class">
        <property name="emailAdresses" ref="emailAdresses" />
    </bean>
    The behaviour is, of course, that one list overrides the other. First I tried to use the parent-child-mechanism to merge the list but there is no parent-attribute for <util:list>.

    What am I doing wrong? Has anybody an example of this?

  • #2
    Did you look at the 2.0 Reference Manual? I think this exact example is used.

    Comment


    • #3
      Yes, I read the Spring 2.0 Reference Manual, and yes, there is a chapter about collection merging (3.3.3.4.1). The example in this chapter works pretty fine for me. Unfortunately, it is based on the usage of the parent-child-relation of two lists and can therefor be used with ListFactoryBean. There is no example for list merging with <util:list> and additionaly the parent-child-relation can not be used because there is no parent-attribute for <util:list>. Any ideas?

      Comment


      • #4
        From the Reference Manual; 3.3.3.4.1. Collection merging
        As of Spring 2.0, the container also supports the merging of collections. This allows an application developer to define a parent-style list, map, set or props element, and have child-style list, map, set or props elements inherit and override values from the parent collection; i.e. the child collection's values will be the result obtained from the merging of the elements of the parent and child collections, with the child's collection elements overriding values specified in the parent collection.
        So, there HAS to be a parent/child relationship in order to merge.

        In your example above, all you are doing is defining two different beans, but using the same id. When this happens, last one wins.

        Comment


        • #5
          Hi,

          well, I guess merging in a parent-child relationship only works if you use old-style spring xml declaration. Spring 2.0 XSD for the <util:/> namespace does indeed not provide a parent attribute which might be needed for the parent-child relationship.

          Am I missing something here. I am also unable to merge map beans in Spring 2.0.

          Regards
          dokmatik

          Comment


          • #6
            Is there a built-in way to merge multiple lists?

            Hi everyone,
            I have defined several lists using util:list and now I want to merge all of them into one list. How can I do that?
            The problem I am trying to solve is: I have several components coming in JAR files, each of them uses Spring+Hibernate and defines the mapping resources as a list that can be referenced by external Hibernate session factory bean definition.
            Now I'm building this application that uses these JARs and I need to define a Hibernate session factory bean with the mapping resources being the merge of all these existing lists. Otherwise, I have to copy the content of the mapping resources list from each JAR and merge them manually.
            Your advice is greatly appreciated. :-)
            Alex

            Comment


            • #7
              Hello, I also am trying to do the same thing. Has anyone been successfully able to do this?

              Comment


              • #8
                Some Links, but no solution

                I also wanted to use this feature, but am too lazy to dig through the code. According to the documentation on <util:list/> (see the end of the section) this should work:
                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 entitled Section 3.3.2.4.1, “Collection merging”.
                The schema (2.5) does not allow the merge attribute. I also had a look at the context file for the (in my interpretation) relevant test case: testUtilNamespace.xml (rev 1.21). I cannot see any hint on how to use collection merging.

                Is there anyone who can bring some insight into this topic? I would raise a JIRA issue if I knew how to raise it in this situation.

                Comment


                • #9
                  Originally posted by kariem View Post
                  Is there anyone who can bring some insight into this topic? I would raise a JIRA issue if I knew how to raise it in this situation.
                  It seems this has just been forgotten. So yes, I'd just raise a Jira issue at Spring Jira.

                  Joerg

                  Comment


                  • #10
                    Found JIRA Issue

                    Thank you for the reply, Jörg.

                    I actually found an old forum post, and a related issue: SPR-2887. According to the issue, this should be fixed in Spring 3.0 M1.

                    Comment


                    • #11
                      New Implementation

                      I have created an implementation that will merge any number of "collections" into a parent collection to create a single collection. This should be easily patched into the current spring-utils based version if needed. Here is the test spring config for an example.

                      Code:
                      <?xml version="1.0" encoding="UTF-8"?>
                      <beans xmlns="http://www.springframework.org/schema/beans"
                             xmlns:collections="http://www.ccanning.com/spring/collections"
                             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                             xsi:schemaLocation="
                                http://www.springframework.org/schema/beans
                                http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                                http://www.ccanning.com/spring/collections
                                http://www.ccanning.com/spring/collections-1.0.xsd">
                      
                        <collections:list id="parentList">
                          <value>1</value>
                          <value>2</value>
                        </collections:list>
                      
                        <collections:list id="childList1" parent="parentList">
                          <value>3</value>
                          <value>4</value>
                        </collections:list>
                      
                        <collections:list id="childList2" parent="parentList"">
                          <value>5</value>
                          <value>6</value>
                          <value>2</value>
                        </collections:list>
                      
                        <collections:set id="parentSet">
                          <value>1</value>
                          <value>2</value>
                        </collections:set>
                      
                        <collections:set id="childSet1" parent="parentSet">
                          <value>2</value>
                          <value>3</value>
                        </collections:set>
                      
                        <collections:set id="childSet2" parent="parentSet">
                          <value>4</value>
                          <value>3</value>
                        </collections:set>
                      
                        <collections:map id="parentMap">
                          <entry key="1" value="1"/>
                          <entry key="2" value="2"/>
                        </collections:map>
                      
                        <collections:map id="childMap1" parent="parentMap">
                          <entry key="2" value="4"/>
                          <entry key="3" value="3"/>
                          <entry key="4" value="4"/>
                        </collections:map>
                      
                        <collections:map id="childMap2" parent="parentMap">
                          <entry key="4" value="16"/>
                        </collections:map>
                      </beans>
                      This would provide the following parent collections:
                      Code:
                        <collections:list id="parentList">
                          <value>1</value>
                          <value>2</value>
                          <value>3</value>
                          <value>4</value>
                          <value>5</value>
                          <value>6</value>
                          <value>2</value>
                        </collections:list>
                      
                        <collections:set id="parentSet">
                          <value>1</value>
                          <value>2</value>
                          <value>3</value>
                          <value>4</value>
                        </collections:set>
                      
                        <collections:map id="parentMap">
                          <entry key="1" value="1"/>
                          <entry key="2" value="4"/>
                          <entry key="3" value="3"/>
                          <entry key="4" value="16"/>
                        </collections:map>
                      The children also exist as is.

                      Currently, this does not recurse through ancestor chains or validate that. if you want this style of functionality, feel free to use the attached source (full maven build with test).

                      chuck

                      Comment


                      • #12
                        if I understand you right, you're proposing that the 'merged collections' be declared in the components which will be used to construct them. I don't think this is a satisfactory solution for several reasons.

                        First, the ability to create a merged collection is predicated on the ability to modify the declarations of the component collections. This is frequently not available. In my particular use case I need to create a list which is a merge of lists contained in the resources from several different projects, contained in distributed jars and thus the source XML is read only. I suspect that merging of collections from external read only sources is frequently going to be the case if not the norm.

                        Second, this solution ends up creating named beans which aren't explicitly declared in the source xml, at least not as named beans. This seems to go against the design philosophy of spring, where if you didn't declare an object with a given id attribute, you can't get it out of the context.

                        Third, how do you create multiple merged collections from the same source collections? You can't have more than one parent attribute and creating a new set of rules for parsing the contents of the parents attribute presents a whole new level of complexity

                        There's a number of other unanswered questions...

                        What happens if you have a name collision between the phantom collection created and an explicitly declared bean? This would probably result in an initialization error, but it would be hard for spring config editors to catch beforehand without making them significantly more complex.

                        What happens if you're building an ordered collection from several different beans. What order will the final collection be in? It seems at first glance that it would simply end up being declaration order, but that has a issues of its own. You don't want to have to start re-ordering other parts of your spring config or possibly other spring configs which you're including in order to achieve a given order. Even worse, what if you have multiple collections which need to be merged into different orders from the same source collections.

                        While turning this problem on its head as you've done does present certain advantages from an implementation perspective, I believe it will be difficult to tie up all the loose ends and end up needing lots of caveats in the documentation making it overly complicated to use. The goal of spring should be easy to understand and use no matter how hard it is to code.

                        Comment


                        • #13
                          Sorry for the late reply.

                          No, I am not implying that they be defined in the same resource, on the contrary, it is to define them in multiple configuration files. That is our use case. This solution sticks with the design methodology of the collections factory bean design to allow collections to be merged into a parent collection wherever they are defined.

                          On your second point, all of the collections have an id and can be assigned.

                          On your third point, there are two methods to solve this:
                          1) have a single parent and multiple children, this is how we use it
                          2) I could easily add recursion to merge up the ancestor chain (I left this out because it wasn't a requiremnt for us because 1 was sufficient

                          On your 4th point, with the current implmentation and how the spring parsing is implemented, the last bean definition defined wins. This is a real problem for us as we configure our system as components and you import the config for each component. If spring imports the same beans twice, it destroys the existing config and you lose previous merges. I have a fix that checks for duplicate beans (assuming import case) that returns the original so this behavior is not "destructive". This is a gerneal problem with spring, not with this solution.

                          On your 5th point, it occurs the same as spring currently does, the ordered list is built in the order that spring reads in the beans. They will be merged in that order. It wouldn't be that hard to add an order attribute, but using depends-on would be better for this since you have to know your order and that should enforce it.

                          This works like the merge functionality does when you embed lists inside a bean and use the merge feature. I am not turning anything on its head or adding any extra caveats beyond the current implementation (as far as i can tell).

                          Hope this clarifies my solution. If you have more questions, please feel free to post them and I should respond much more quickly as i am not so busy now.

                          Also, feel free to disagree or challenge any of these points. The best solution is one that works for everyone.

                          Comment


                          • #14
                            uitl:list and spring @autowire behave strangely.

                            I have rather different experience.

                            here is an example
                            I have person class in my configuration file I have say following bean definitions.

                            Code:
                             <bean id="person1" class="com.test.Person"/>
                            
                               <bean id="personA" class="za.co.discovery.util.Person"/>
                               	   
                               <bean id="personB" class="za.co.discovery.util.Person"/>
                            	 
                            
                                     <util:list id="persons">
                            		<ref bean="personA" />
                            		<ref bean="personB" />
                            	</util:list>
                            in my code if I put @Autowired

                            Code:
                            @Autowired
                            	List<Person> persons;
                            All I get in the list personA , personB and surprisingly person1 . with autowired list will give me all the beans person declared inside application context. My List size showing me 3 instead of 2

                            if I declare one more list


                            Code:
                            <bean id="person1" class="com.test.Person"/>
                            
                               <bean id="personA" class="za.co.discovery.util.Person"/>
                               	   
                               <bean id="personB" class="za.co.discovery.util.Person"/>
                            
                            <bean id="personC" class="za.co.discovery.util.Person"/>
                            
                            
                            <bean id="personD" class="za.co.discovery.util.Person"/>
                            
                            
                            <util:list id="persons">
                            		<ref bean="personA" />
                            		<ref bean="personB" />
                            	</util:list>
                            
                            <util:list id="persons2">
                            		<ref bean="personC" />
                            		<ref bean="personD" />
                            	</util:list>
                            
                            @Autowired
                            	List<Person> persons;
                            
                            @Autowired
                            	List<Person> persons2;
                            both list showing me size 5 that means all the person's beans inside xml i.e. 5 instead of 2.

                            but if I do context.getBean("persons") give me size 2 and context.getBean("persons2")
                            give me size 2 which is proper behavior

                            is it bug ????

                            I have tested both spring 2.5.6 and spring 3.1.0.M1 same result.

                            Comment


                            • #15
                              I am also observing the same behavior has rajesh_parab and I am using 3.2.4. Is this a bug?

                              Comment

                              Working...
                              X