13 Replies Latest reply on Aug 28, 2006 2:44 PM by Dave Whittaker

    A couple of observations

    Dave Whittaker Novice

      Now that I've been using Seam for a while, I wanted to point out a couple of points that I've had trouble with or thought might be useful enhancements just to see if anyone else out there agrees/disagrees/or has any suggestions of alternatives.

      1)I haven't found a good way to instantiate multiple, unique Seam components of the same type. As a use case say that I have a ProductFinder SFSB that uses multiple CategoryFilter objects to narrow down a result list. I can't create a subclass of CategoryFilter for each type of category because the categories are dynamically defined, and I'd like them to be Seam components so that they can take advantage of bijection and all the other Seam goodies. What I've been doing in this situation is using Component.newInstance(), but besides the fact that Gavin indicated that wasn't a great idea, it doesn't really seam suited to the task anyway. For one thing, newInstance() will cause an outjection of the created component after it is created, which is not really something I'm interested in for this scenario. Why clutter up any context when the bean is just used internally by the containing class? This isn't much of a problem with EJBs since they can be retrieved outside of seam and still be intercepted, but how about a method like T Component.newInstance(Class beanClass) to instantiate a CGLIB enhanced bean of any class with a no-arg constructor? The instantiated bean wouldn't even have to be named since it won't be outjected. Or at least public access to the methods on Component necessary to do something like that would be helpful.

      2) Wouldn't the ability to define an automatic naming convention be nice? Something like the type of pattern Log4J gives you to output a portion of a class name? Generally, I'm naming all of my beans to be the camel case class name with a lowercase first character and this would help with refactoring as well as stupid mistakes like copying a similar object and forgetting to change the name. Hopefully I'm not the only one who does things like that. Basically, all you would have to do is give the component a @Name annotation with no value to get the default. I'd imagine the naming convention could be defined in seam.properties.


      Anybody else interested?

        • 1. Re: A couple of observations
          Ângelo Luis Newbie

          if i understand 1 , you want to do somthing like this:

          @..
          private A a;
          @...
          private A b;

          bijection with the same type??


          i can't do it work... if the 1 is my problem, yes this is interesting

          • 2. Re: A couple of observations
            Dave Whittaker Novice

             

            f i understand 1 , you want to do somthing like this:

            @..
            private A a;
            @...
            private A b;

            bijection with the same type??


            Not exactly, I think that actually can be done if you use the @Role annotation within class A (although I'm not crazy about that, why should A have to know about how it's going to be used?). What I'm thinking is more along the lines of:

            private List aObjects;

            where I then want to populate aObjects with unique instances of A, possibly in a factory method and have each object be properly intercepted.

            • 3. Re: A couple of observations
              Pete Muir Master

               

              "iradix" wrote:

              1)I haven't found a good way to instantiate multiple, unique Seam components of the same type. As a use case say that I have a ProductFinder SFSB that uses multiple CategoryFilter objects to narrow down a result list. I can't create a subclass of CategoryFilter for each type of category because the categories are dynamically defined, and I'd like them to be Seam components so that they can take advantage of bijection and all the other Seam goodies. What I've been doing in this situation is using Component.newInstance(), but besides the fact that Gavin indicated that wasn't a great idea, it doesn't really seam suited to the task anyway. For one thing, newInstance() will cause an outjection of the created component after it is created, which is not really something I'm interested in for this scenario. Why clutter up any context when the bean is just used internally by the containing class? This isn't much of a problem with EJBs since they can be retrieved outside of seam and still be intercepted, but how about a method like <T> T Component.newInstance(Class<T> beanClass) to instantiate a CGLIB enhanced bean of any class with a no-arg constructor? The instantiated bean wouldn't even have to be named since it won't be outjected. Or at least public access to the methods on Component necessary to do something like that would be helpful.


              I can see uses for this, but remember you can do bijection etc. through Component and the Context. Since you're creating a reusable component this might be a better way to do it as it allows you to specify context variable names at runtime rather than compiletime. Is there anything you can do with Seam annotations that you can't with methods? Off the top of my head: DataBinder stuff perhaps (could be useful), Logger (which I would find useful to be able to access as Logger.getInstance(...) for these situations thus removing a commons-logging dependencies).

              2) Wouldn't the ability to define an automatic naming convention be nice? Something like the type of pattern Log4J gives you to output a portion of a class name? Generally, I'm naming all of my beans to be the camel case class name with a lowercase first character and this would help with refactoring as well as stupid mistakes like copying a similar object and forgetting to change the name. Hopefully I'm not the only one who does things like that. Basically, all you would have to do is give the component a @Name annotation with no value to get the default. I'd imagine the naming convention could be defined in seam.properties.


              Yeah. Something similar to the Hibernate Naming Strategy I think would be a good idea.

              • 4. Re: A couple of observations
                Dave Whittaker Novice

                 

                I can see uses for this, but remember you can do bijection etc. through Component and the Context.


                True, but then why not always use method calls and disregard annotations all together? It seems to me that most of the functionality is available programatically, but the annotations, and maybe more importantly the method interception and automatic bijection that go along with them make life a lot easier.

                Also, thanks for pointing out the logger which is another good example of why bijection is still very handy in plain java beans that don't necessarily need to live in a context themselves.

                Yeah. Something similar to the Hibernate Naming Strategy I think would be a good idea.


                Cool. Anybody else?

                • 5. Re: A couple of observations
                  Gavin King Master

                  1) Use @Roles
                  2) Yes, this would make sense but .... we would need to introduce an @Component annotation

                  • 6. Re: A couple of observations
                    Dave Whittaker Novice

                     

                    1) Use @Roles


                    Roles are only useful if you can choose unique names at compile time. In the use case I gave, the filters are built on categories which are dynamically defined through the application so roles won't work. I've got code that looks something like:

                    List<ProductFilter> filters = new ArrayList<ProductFilter>();
                    for(ProductCategory category: categoryDAO.list()){
                     ProductCategoryFilter filter = Component.newInstance("productCategoryFilter");
                     filter.setCategory(category);
                     filters.add(filter);
                    }


                    Besides the fact that Component.newInstance() will outject the created components one after the other, which isn't necesary in this case and might be a problem if I did have a productCategoryFilter outjected to a context for some reason, it also won't do any unwrapping. There just really doesn't seem to be a good way to simply instantiate a seam component so that it is intercepted when you don't need retrieval from a context or outjection to a context and I think this is useful, especially with POJOs.

                    2) Yes, this would make sense but .... we would need to introduce an @Component annotation


                    Sounds good to me. Out of curiosity though, why wouldn't @Name with no value attribute work?



                    • 7. Re: A couple of observations
                      Dave Whittaker Novice

                      You still there Gavin? If you've got a moment I'm curious to know what you think about my issue with @Roles.

                      • 8. Re: A couple of observations
                        Gavin King Master

                        If the names are dynamic, use Context.set(name, object)

                        • 9. Re: A couple of observations
                          Dave Whittaker Novice

                          I think you're misunderstanding my problem. I don't want to put the objects into a Context. I want to instantiate a variable number of unique objects of the same Class, have Seam intercept each of them, and add them to a list. Placing them in a Context is not only unnecessary since they are only accessed through the SFSB that contains them, it is a headache since I can't use Component.getInstance() to retrieve multiple unique objects. Does that make more sense? If the code block above doesn't explain it well enough I will post more.

                          • 10. Re: A couple of observations
                            Rob Jellinghaus Novice

                            Your problem seems to make sense to me. You basically want a method something like:

                            Component.createComponent(componentClass)

                            which would return a Seam-wrapped instance of componentClass that was not stored in any context.

                            The main thing this loses, though, is any ability for Seam to track the lifecycle of these "disconnected components". Since lifecycle tracking (and persistence context handling) is one of Seam's primary benefits, I can see why Gavin isn't too focused on this.

                            • 11. Re: A couple of observations
                              Dave Whittaker Novice

                              I hadn't really thought of that, however in the case where I would want to use something like this the "scope" of the object would be its parent - i.e. the object which created it.

                              I'm not saying that a "createComponent" method is necessarily the way to go, but right now it seems to me that Seam promotes a somewhat monolithic backing bean. In situations where I'd like to delegate the function of my SFSB's to sub-objects I'm dettered from doing so because those sub-objects either must be published to a context or cannot take advantage of bijection. Here's an idea... how about a @ChildComponent annotation specifying that a field should also take part in the bijection of its parent? Kind of like EJB3's @Embedded? Conceptually I actually like that alot and since the @ChildComponent would explicitly live within a parent it would be tied to the parents lifecycle and solve that issue as well. It could also be applied to collections which would solve my original problem. What do you think?

                              • 12. Re: A couple of observations
                                Maurice Zeijen Master

                                Isn't that something like an Inner Class?

                                Is it possible to draw a little class diagram or create some dummy example code because I am not sure if I 'get' you.

                                • 13. Re: A couple of observations
                                  Dave Whittaker Novice

                                  Not an inner class, the class definition would not have to be within the parent.

                                  An example of how it works now:

                                  @Stateful
                                  @Name("productSearcher")
                                  public Class ProductSearcherImpl....{
                                  
                                   @DataModel
                                   private List<Product> searchResults;
                                  
                                   private List<ProductFilter> filters = new ArrayList<ProductFilter>();
                                  
                                   @Create
                                   public void init(){
                                   for(ProductCategory category: categoryDAO.list()){
                                   ProductCategoryFilter filter = Component.newInstance("productCategoryFilter");
                                   filter.setCategory(category);
                                   filters.add(filter);
                                   }
                                   }
                                  
                                   @Factory("searchResults")
                                   public void search(){
                                   searchResults = ...
                                   for(ProductFilter filter: filters){
                                   filter.apply(searchResults);
                                   }
                                   }
                                  
                                  }
                                  


                                  To me at least this is bad. Every time I call Component.newInstance() I outject the created filter. Since the Filter is never used outside of the containing SFSB, I don't want it oujected. What I do want is for each filter to recieve injection from the associated contexts AND be allowed to outject values into those contexts. What I was thinking would look like this:

                                  @Stateful
                                  @Name("productSearcher")
                                  public Class ProductSearcherImpl....{
                                  
                                   @DataModel
                                   private List<Product> searchResults;
                                  
                                   @ChildComponent
                                   private List<ProductFilter> filters = new ArrayList<ProductFilter>();
                                  
                                   @Create
                                   public void init(){
                                   for(ProductCategory category: categoryDAO.list())
                                   ProductFilter filter = new ProductFilter(category);
                                   }
                                  
                                   @Factory("searchResults")
                                   public void search(){
                                   searchResults = ...
                                   for(ProductFilter filter: filters){
                                   filter.apply(searchResults);
                                   }
                                   }
                                  
                                  }
                                  


                                  Does that make more sense?