14 Replies Latest reply on Aug 17, 2007 4:34 AM by porcherg

    ReferenceDescriptor and init

    porcherg

      the ReferenceDescriptor has a field

      isDelayedInitializationAllowedForReferredObject

      It defines if the referenced object should be eagerly initialized during the construction of the ReferenceDescriptor. This field is not updated by the parser.

      As it extends AbstractDescriptor, it has a field
      isEagerInit
      . This field is set by the parser according to the "init" attribute.
      What is the meaning of this field in this case ? Maybe we can used it instead of 'isDelayedInit...' ?


      Regards, Guillaume

        • 1. Re: ReferenceDescriptor and init
          porcherg

          I think I was wrong.

          The init='eager' attribute is for creating objects when the context is created.

          The isDelayedInit.. is to specify if the initialization phase should be done during the creation of the object or later.

          But I don't see where the isDelayedInit.. field is set (by property injection ?)

          • 2. Re: ReferenceDescriptor and init
            porcherg

            I have the same problem with ObjectDescriptor. The default value for delayedInitialization is enabled.

            In the following test, this delayedInit will result in a NullPointerException:

             public static class NeedInit {
             private List l;
            
             public int getSize() {
             return l.size();
             }
             }
            
             public void testInit() {
             WireContext wireContext = createWireContext(
             "<objects>" +
             " <object name='o' class='"+NeedInit.class.getName()+"'>" +
             "<field name='l'>" +
             " <list /> " +
             "</field>" +
             " </object>" +
             " <object name='q' factory='o' method='getSize' />" +
             "</objects>"
             );
             Object q = wireContext.get("q");
            
             assertNotNull(q);
             assertEquals(Integer.class, q.getClass());
             }
            


            If we disable delayedInit (modification of the default value since I don't know how to do it in xml), the test works.


            Has this use case a real application in this project ?

            I don't really understand the delayedInit. When can we use an object that is not yet initialized ?


            Regards, Guillaume

            • 3. Re: ReferenceDescriptor and init
              porcherg

              The previous example can be fixed by forcing immediate init for the factory object 'o' (a small correction in ObjectDescriptor)

              But in some situations, the default values for isDelayedInitializationAllowed don't work.

              I've added a new test file DelayedInitTest, which shows the problem:
              - testBidirectionnalInit() tests initialization of a bidirectionnal dependency (it's also in DependencyTests but I just wanted to put all in the same file)
              - testRefAndDelayedInit() is using a ReferenceDescriptor as a factory (I don't know if it is a real use case)

              With the default settings, the first test works, the second fails.

              If we set isDelayedInitializationAllowedForReferredObject = false in RefDescriptor, the first one fails and the second one succeeds.

              If we set isDelayedInitializationAllowedForReferredObject = true in RefDescriptor, and isDelayedInitializationAllowed = false in objectDescriptor, the two tests succeeds.

              In this configuration, an object is initialized during the construction by default, and if it is used in a reference, we allow the init to be delayed in order to resolve circular dependencies (in bidirectionnal associations for example)

              Does this approach work ?

              Regards,
              Guillaume

              • 4. Re: ReferenceDescriptor and init
                tom.baeyens

                so the first thing that needs to be added is parsing the isDelayedInitializationAllowed property.

                what would you think of the following:

                init='eager' for eager initialization upon WireContext construction
                
                init='required' for objects that need to be immediately initialized after construction.
                


                then we could refactor properties isDelayedInitializationAllowed and isEagerInit into 1 char property in AbstractDescriptor:

                public static final char INIT_LAZY = 'L';
                public static final char INIT_EAGER = 'E';
                public static final char INIT_REQUIRED = 'R';

                protected char init = INIT_LAZY;

                parsing would have to be adjusted accordingly.

                what do you think?



                FYI: Note that the eagerInitNames in WireContext are for persistence performance. In theory, we could just loop over all the descriptors and ask if they have to be eagerly initialized. But when hibernate loads the descriptors map, it returns a map with all proxies. They are only loaded the first time they are accessed. So if only 2 out of 10 objects have to be eager loaded, for the 8 others the descriptor will be initialized.

                • 5. Re: ReferenceDescriptor and init
                  porcherg

                  I think we have 4 possible values for the init property:
                  - creation upon WireContext creation and immediate initialization
                  - creation upon WireContext creation and delayed initialization
                  - lazy creation, but immediate initialization ( INIT_REQUIRED ?)
                  - lazy creation and delayed initialization (INIT_LAZY ?)

                  I don't know which one is INIT_EAGER (maybe eager creation + immediate init ?)

                  Maybe we could use two attributes "init=required|delayed" and "create=eager| lazy" ?

                  What do you think of setting delayedInit to false in the ObjectDescriptor, and to true in ReferenceDescriptor by default ?
                  I don't have example in tests where this configuration doesn't work (and we would still have the xml attributes to override the default settings)


                  Regards,
                  Guillaume

                  • 6. Re: ReferenceDescriptor and init
                    csouillard

                     

                    "porcherg" wrote:
                    I think we have 4 possible values for the init property:
                    - creation upon WireContext creation and immediate initialization
                    - creation upon WireContext creation and delayed initialization
                    - lazy creation, but immediate initialization ( INIT_REQUIRED ?)
                    - lazy creation and delayed initialization (INIT_LAZY ?)


                    Tom,
                    can you confirm that please ?
                    We are trying to find good names for init attribute value...
                    Have you got an idea ?
                    Thanks
                    Charles

                    • 7. Re: ReferenceDescriptor and init
                      tom.baeyens

                       

                      "porcherg" wrote:
                      "init=required|delayed" and "create=eager| lazy"


                      i don't think that is good language design. as mostly people will have a simple basic requirement: initialize the object on startup. then providing them with 2 attributes that they have to put in the right combination is too complex.

                      for objects that are eagerly initialized, i don't think it is important to have control over the delayed initialization. because it will typically be sessionFactories and other static resource. They are typically (never?) so tightly coupled that you need to resolve bidirectional references.

                      eager initialization is ortogonal to required initialization as you also indicate. but required initialization can be specified in many different locations:

                      the descriptor: the descriptor can say: don't deliver this object to anyone unless it's fully initialized.

                      ref usage: if you use a ref element somewhere you can say: for this reference, i only want the object when it's fully initialized, because i might want to start using it directly. e.g. when an object is injected in a constructor or method invocation.

                      the internal API: the interal API also has an optional boolean to say "the object must be initialized when you give it to me". I believe that this was added only for the ref usage discussed above, but i'm not sure.

                      that's the background. now for my counter proposal:

                      i would like to keep the init='eager' thing.

                      and for object and ref elements, we need a way to specify that initialization must be done immediate.

                      maybe we can use the same init attribute for that...

                      <object init='immediate'
                      means that the object is always immediately initialized after construction.

                      <ref init='immediate'
                      means that the referenced object is always initialized before it is used in this reference.

                      and for really exceptional situations, you can specify

                      <object init='eager, immediate'
                      means that the object is eagerly initialized and always immediately initialized after construction.

                      WDYT ?

                      • 8. Re: ReferenceDescriptor and init
                        porcherg

                        I tried to introduce a init attribute with 4 values:
                        - "immediate" for eager creation and immediate init
                        - "eager" for eager creation and delayed init
                        - "required" for lazy creation and immediate init
                        - "lazy" for lazy creation and delayed init

                        This way we have only one char variable to store this information (as you mentioned before).

                        I can change in the parsing the name of the attribute value to have "eager, immediate" instead of "immediate" and "immediate" instead of "required".
                        This way we would have two orthogonal properties 'eager/lazy' and 'immediate/delayed' and the default value for init is 'lazy, delayed'. Specifying init="eager" would mean init='eager, delayed' and init='immediate' would mean init='lazy, immediate'

                        In the Object Descriptor, we can add that an object used as a factory (in attribute or in element) must not be delivered unless it's fully initialized. This was the case for the factory element, but not for the factory attribute.

                        What do you think about this solution ?


                        I have a problem with the initialization procedure: I think the following test should succeed, but it fails:

                        public void testBidirectionnalInitBRequired(){
                         WireContext wireContext = createWireContext(
                         "<objects>" +
                         " <object name='a' class='"+A.class.getName()+"'>" +
                         " <field name='b'>" +
                         " <ref object='b' init='required'/>" +
                         " </field>" +
                         " </object>" +
                         " <object name='b' class='"+B.class.getName()+"'>" +
                         " <field name='a'>" +
                         " <ref object='a'/>" +
                         " </field>" +
                         " </object>"+
                         "</objects>"
                         );
                        
                        
                         Object b = wireContext.get("b");


                        "b" is created. During its initialization, "a" is created, but not initialized. So "b" is fully initialized.
                        Then "a" is initialized and requires "b". This test should be ok.

                        In the execution, when 'b' creates 'a', 'a' is initialized (by the processPendingInitialization method) before the end of initialization of 'b'. And so a new 'b' is constructed during the init of 'a'. As a result, we have
                        b.a.b != b
                        .

                        I will try to work on this problem.


                        regards,
                        Guillaume

                        • 9. Re: ReferenceDescriptor and init
                          tom.baeyens

                           

                          "porcherg" wrote:
                          I tried to introduce a init attribute with 4 values:
                          - "immediate" for eager creation and immediate init
                          - "eager" for eager creation and delayed init
                          - "required" for lazy creation and immediate init
                          - "lazy" for lazy creation and delayed init

                          This way we have only one char variable to store this information (as you mentioned before).


                          OK.

                          (you could still have 2 attirbutes and 1 char member field, but I like your proposal better)

                          "porcherg" wrote:
                          I will try to work on this problem.


                          let me know how it goes

                          • 10. Re: ReferenceDescriptor and init
                            porcherg

                            I modified the initialization. Let me know if I have made something wrong.

                            - the default get(objectName) returns a immediately initialized object. It's the method called when we search something in the environment.
                            - the get(objectName, delayedInit = true) returns a constructed object, but not initialized (unless specified in the descriptor)
                            When an object is created, if it is not initializable, it is registered in the cache and returned. If it is initializable, and delayed is allowed, it is added to PendingInitializations and returned.
                            After an object is registered in the cache, we perform initialization of pendingInitializations, only if no other object is under initialization (this makes the previous example work).

                            With this modification all the previous tests succeeds.

                            If you see no big mistake in my description, I will check in my changes so that you can see more clearly what I've modified.

                            Guillaume

                            • 11. Re: ReferenceDescriptor and init
                              porcherg

                              I have submitted the modifications for
                              - the init attribute (4 possible values 'immediate', 'eager' 'required' and 'lazy')
                              - the initialization in WireContext

                              Let me know if something is not working.

                              I will add tests for this.

                              Guillaume

                              • 12. Re: ReferenceDescriptor and init
                                tom.baeyens

                                 

                                "porcherg" wrote:
                                Let me know if something is not working.


                                did you add a test case that checks all 4 configurations ?

                                • 13. Re: ReferenceDescriptor and init
                                  porcherg

                                   

                                  "tom.baeyens@jboss.com" wrote:
                                  did you add a test case that checks all 4 configurations ?


                                  'eager' and 'lazy' init were already tested in the wire test suite.
                                  I have added tests for 'required' init (in DelayedInitTest)

                                  I have not tested init='immediate' yet. I'll add a test for this one.


                                  Guillaume

                                  • 14. Re: ReferenceDescriptor and init
                                    porcherg

                                    I have added tests in EagerInitTest
                                    testEagerInitializationListSequence, testImmediateInitializationListSequence, testImmediate2InitializationListSequence
                                    These tests shows that the order of initialization of the elements of two lists is changed if we use eager or immediate initialization.

                                    Writting these tests showed a bug in the processPendingInitializations: as we duplicate the pendingInitializations set, we did not check if the init we are processing was already processed from elsewhere in the code.
                                    I have fixed this by checking that the removed element from the pendingInitializations was not null.

                                    I think I will try to write more tests mixing different init strategies to find more bugs.

                                    regards,
                                    Guillaume