1 2 Previous Next 15 Replies Latest reply on Nov 3, 2009 4:50 AM by Steven Boscarine

    How do I get get store a reference to an @Inject-ed variable and not it's proxy?

    Steven Boscarine Apprentice

      Hello All,
      I want to save the value from an @Inject annotation into a list.  How can I save a reference to the bean and not the proxy?


      I am trying to do a simple Weld POC that involves saving a simple bean to a List.  Here's my service class:


      @ApplicationScoped
      @Named
      public class CategoryServiceBean implements Serializable {
           private final List<Category> categories = new ArrayList<Category>();
      
           public List<Category> getCategories() {
                return categories;
           }
      
           public void create() {
                categories.add(category);
           }
      
           @Inject
           private Category category;
           private static final long serialVersionUID = 1L;
      }



      I have an ultra-crude CRUD app that's wired to this bean.  Category is a bean with 1 property.  There's a page with a form and a list of categories. 



      ...
                <h:form>
                     <h:panelGrid columns="4">
                          <h:outputLabel for="app">Category Name</h:outputLabel>
                          <h:inputText id="app" value="#{category.name}" />
                          <h:message for="app" />
                          <h:commandButton value="Create Application" action="#{categoryServiceBean.create}" />
                     </h:panelGrid>
                </h:form>
                <ul>
                <ui:repeat var="cat" value="#{categoryServiceBean.categories}">
                     <li>#{cat.name}</li>
                </ui:repeat>
                </ul>
      ...
      




      I save a category named 'foo', I see:



      • foo



      I save a category named 'bar', I see:



      • bar

      • bar



      I save a category named 'fubar', I see:



      • fubar

      • fubar

      • fubar



      I assume @Inject is passing a reference to the proxy. 


      If I just want the reference to the bean, itself (foo, bar, & fubar), what's the best way to do so? 


      I know I can manually copy the values, but I am guessing that there's probably a built-in means to get the object reference and not the proxy reference already. 


      Thanks!

        • 1. Re: How do I get get store a reference to an @Inject-ed variable and not it's proxy?
          Nicklas Karlsson Master

          Are you sure your Conversation bean is short-lived enough (request)? If it lives too long, it is created the first time it is referenced and then gets added to list but they all refer the same instance so you end up with a mutating value for all entries...

          • 2. Re: How do I get get store a reference to an @Inject-ed variable and not it's proxy?
            Steven Boscarine Apprentice

            Hello Nicklas,

            Thanks for responding.  Category already was set to @RequestScope.  Do you have any idea of what I should try next?


            Is this expected behavior?
              If so, is there an API call to get de-proxy the bean?


            Thanks!


            @Named
            @RequestScoped
            public class Category implements Serializable {
                 private String name = "default";
            
                 public String getName() {
                      return name;
                 }
            
                 public void setName(String name) {
                      this.name = name;
                 }
            
                 @Override
                 public String toString() {
                      return "Category(" + name + ")";
                 }
            
                 private static final long serialVersionUID = 1L;
            }



            • 3. Re: How do I get get store a reference to an @Inject-ed variable and not it's proxy?
              Nicklas Karlsson Master

              Well, dependent-scoped stuff isn't proxied at all so perhaps that does the trick.

              • 4. Re: How do I get get store a reference to an @Inject-ed variable and not it's proxy?
                Steven Boscarine Apprentice

                That didn't work.  I didn't see any uses of @Dependent in the examples, so I am not certain on how it's supposed to be used.  Were you suggesting this?:


                @Named
                //@RequestScoped
                @Dependent
                public class Category implements Serializable {



                My app wasn't storing the values I inputed through JSF.


                I'd enter 'foo' and it would save 'default', the String I initialize it to. 


                Is the service class correct?  I simply wanted to take the input from the form & save it to that list. 

                • 5. Re: How do I get get store a reference to an @Inject-ed variable and not it's proxy?
                  Nicklas Karlsson Master

                  You could have a Category in the bean (not injected) and bind the EL into it. I think this proxy-stuff would be the same if you would do it in Seam...

                  • 6. Re: How do I get get store a reference to an @Inject-ed variable and not it's proxy?
                    Steven Boscarine Apprentice

                    I'm sorry, I am coming to WebBeans from Spring WebFlow + JSF.  I am not too familiar with how Seam does things. 


                    Won't that either break concurrency or persistence, depending on how you do it? 


                    I wanted a collection, wrapped in CategoryServiceBean, to be @ApplicationScoped so it can be the persistence mechanism in this piece of code.  I wanted Category to bind to the form values, so I assume it needs to be @RequestScoped. 


                    If I remove @Inject on Category and on CategoryServiceBean & expose a getter & setter, Category is now @ApplicationScoped, isn't it?

                    That means 2 users cannot user the system simultaneously without risk of overwriting values, correct?


                    I assume it'd work if I removed @Inject and added getters + setters and changed @ApplicationScoped to be @RequestScoped, however, that would obviously break persistence. 


                    Since you mentioned Seam...


                    So what's the best practice for handling form input?  From the number guess examples, I assumed it'd be as seen above where an @ApplicationScope-annotated service is bound to a JSF button and JSF fields are bound to a @RequestScope annotated bean that is linked to the service via the @Inject annotation.


                    SWF does something conceptually similar, except the form bean is usually passed as a parameter to a singleton @Service.


                    Assuming the behavior of passing the proxy is expected, is the best practice to simply copy the values to never use objects passed via the @Inject annotation beyond the bean that holds the @Inject annotation (CategoryServiceBean)?







                    • 7. Re: How do I get get store a reference to an @Inject-ed variable and not it's proxy?
                      Huy Le Newbie

                      Perhaps you can use a conversation scoped bean and expose a getter on that, e.g.



                      @ConversationScoped
                      @Named
                      public class CategoryAction implements Serializable {
                           
                         private static final long serialVersionUID = 1L;
                         
                         private Category category;
                         @Inject
                         private Conversation conversation;
                         @Inject
                         private CategoryServiceBean service;
                      
                         @PostConstruct
                         protected void initialise() {
                            category = new Category();
                            conversation.begin();
                         }
                      
                         public Category getCategory() {
                            return category;
                         }
                      
                         public void create() {
                            service.createCategory(category);
                            conversation.end();
                         }
                      
                      }






                      @ApplicationScoped
                      public class CategoryServiceBean implements Serializable {
                           private final List<Category> categories = new ArrayList<Category>();
                      
                           public List<Category> getCategories() {
                                return categories;
                           }
                      
                           public void createCategory(final Category category) {
                                categories.add(category);
                           }
                      
                           private static final long serialVersionUID = 1L;
                      }



                      • 8. Re: How do I get get store a reference to an @Inject-ed variable and not it's proxy?
                        Gavin King Master

                        So, I have a few comments on the first code example you posted, where you injected a Category (which I assume was declared @Entity, and did not explicitly specify a scope).


                        (0) Your problem has nothing to do with proxy references. The explanation is much more basic.


                        (1) I think you did exactly the right thing by not specifying a scope for the entity. Entities should almost always be dependent objects - unless you really know what you're doing.


                        (2) You screwed up by injecting it into an @ApplicationScoped object. A dependent object of an @ApplicationScoped object is essentially a singleton. You get the same object on every request.


                        (3) What you are really trying to do is have a request scoped Category. Since I'm advising you against declaring a scope for the Category, how can you achieve this? Well, the obvious thing is to inject it as a dependent object of a @RequestScoped bean. Something like:


                        @RequestScoped @Named
                        public class RequestCategory {
                            @Inject Category category;
                            public Category getCategory() { return category; }
                            ...
                        }



                        (5) Now, you can write your action method either directly on the RequestCategory class, or on any other class:


                        @Named @ApplicationScoped
                        public class Categories {
                           @Inject RequestCategory requestCategory;
                           List<Category> categories = new ArrayList<Category>();
                           public void addCategory() {
                              categories.add(requestCategory.getCategory());
                           }
                        }


                        • 9. Re: How do I get get store a reference to an @Inject-ed variable and not it's proxy?
                          Gavin King Master

                          Oh and in your JSF page you can use #{requestCategory.category}. You hate that? Hrm, well there is a way around...


                          @RequestScoped @Named
                          public class RequestCategory {
                              @Inject Category category;
                              
                              @Produces @Named @Current
                              public Category getCategory() { return category; }
                              ...
                          }



                          Where @Current is a qualifier you wrote.

                          • 10. Re: How do I get get store a reference to an @Inject-ed variable and not it's proxy?
                            Gavin King Master

                            By the way, I should say that entities are a significantly special case, and the basic contextual lifecycle management doesn't work especially well for them. However, we'll be writing a portable extension to provide nice management for contextual entities. I'm not sure yet precisely what it will look like, but I have a basic idea of what I want out of it.

                            • 11. Re: How do I get get store a reference to an @Inject-ed variable and not it's proxy?
                              Steven Boscarine Apprentice

                              Hello Gavin,

                              Thank you for the multiple responses.  I appreciate the time you're taking to answer these.  It appears my initial approach was wrong, but I am confused as to the approach you are advocating. 


                              I am trying to build a prototype as a proof of concept to confirm we can do our next major project in Weld. 


                              If I wanted to write the simplest Weld/JSF application that saves values, how would I do so?
                                In the example above, I was going to use an in-memory list.  Initially, they are not JPA Entities. 


                              The motivation is HIPAA compliance as all patient data must be strongly encrypted and we can apply heavy-duty encryption very cheaply (from a labor perspective) using an in-memory, serialized data store.  (We're supporting relatively low-budget clinical research, so many of the researchers we work with don't have skilled DBAs to support them...the more the app self-manages, the better)


                              back to the app...


                              Are you advocating, creating:



                              1. A simple entity bean (not JPA in this case) that holds the values, Category and has no CDI annotation?

                              2. A @RequestScoped backing bean, RequestCategory, that appears to only hold a reference to the Category and declare the scope.

                              3. An @ApplicationScoped service bean? in which you inject the consumers of the bean, such as RequestCategory?



                              I apologize if I am being slow here, but I don't really grasp the pattern.


                              I see 3 logical tiers:



                              1. the entity bean, the vessel for user input, in which most examples I see usually put minimal business logic in the entity beans. 

                              2. the service layer, the code that handles business logic

                              3. a data layer, in this case an ArrayList, but in most cases, JPA plumbing



                              Should the entity bean have no CDI annotations?

                              Should the service layer be @RequestScoped?  (It seems heavy, but realistically, the cost is probably trivial.)

                              Shouldn't the data layer be injected into the service layer?





                              • 12. Re: How do I get get store a reference to an @Inject-ed variable and not it's proxy?
                                Gavin King Master

                                Look, the simplest (and likely best) way to look deal with this (right now, until we get some additional infrastructure in place as part of Seam3) is to simply just not treat entities as contextual objects. Entities have their whole own identity and lifecycle model which just doesn't map directly into the 299 context model.


                                Remember, it's simply not enough to say give me the current Category. Which is the current category? What is it's id? What if I have a whole list of Categorys that I'm working with? In the CDI world, there are contextual beans, and non-contextual beans. Not every bean is contextual.


                                Instead, access your entities via a layer of contextual beans - view the entities as belonging to these contextual beans, have them instantiate the entity using new, and manage them just like they would manage any other piece of internal state (Strings, etc.).


                                Earlier drafts of 299 actually explicitly prohibited entities from being managed beans, though we removed that restriction because it was a bit unnecessary.



                                If I wanted to write the simplest Weld/JSF application that saves values, how would I do so? In the example above, I was going to use an in-memory list. Initially, they are not JPA Entities.

                                You need to separate request-scoped state (the entity you are working with in the UI) from application scoped state (your in-memory list or the database). Whatever you do, you are going to need a request-scoped bean (which should not be the entity itself) and another bean that gives you access to the shared state (either your application scoped in-memory list, or a JPA EntityManager). You were trying to do this with one too few objects :-)



                                A simple entity bean (not JPA in this case) that holds the values, Category and has no CDI annotation?

                                Right, exactly. Just use new on this thing. That's the lifecycle model that JPA defines :-)



                                A @RequestScoped backing bean, RequestCategory, that appears to only hold a reference to the Category and declare the scope.

                                Yes. You are almost always going to need a bean that holds request-scoped state. In this case, including the Category that you are trying to create in this request.



                                An @ApplicationScoped service bean? in which you inject the consumers of the bean, such as RequestCategory?

                                It's not clear to me precisely what role you envisage for the @ApplicationScoped bean. If what you are saying is that you have some application-global state (a list of Categorys) that needs to be shared between all users, then yes, of course you are going to need an @ApplicationScoped bean to define that state.


                                Now, exactly how you divide responsibilities for service(s) between the request scoped thing and the application scoped thing is an architectural matter for you to determine when you design your application. You could have request-scoped actions that do most of the work upon dumb application scoped value-holding objects, or you could try to start adding reusable business logic to the application scoped value holders.


                                Just be aware that in a real application, most state doesn't just get chucked in the application context like that. Really it lives in the database.

                                • 13. Re: How do I get get store a reference to an @Inject-ed variable and not it's proxy?
                                  Gavin King Master

                                  I see 3 logical tiers:

                                      
                                  1. the entity bean, the vessel for user input, in which most examples I see usually put minimal business logic in the entity beans.

                                  2.   
                                  3. the service layer, the code that handles business logic

                                  4.   
                                  5. a data layer, in this case an ArrayList, but in most cases, JPA plumbing




                                  I don't think of these as layers, just as roles. Whatever, the point is that layers/roles 2 and 3 have contextual objects. Layer/role 1 doesn't.


                                  Now, again, I plan to have some cool portable extension in Seam3 that changes this picture somewhat, but you don't need that to solve the problem you're working on right now.

                                  • 14. Re: How do I get get store a reference to an @Inject-ed variable and not it's proxy?
                                    Gavin King Master

                                    I think I'm making this look waaay more difficult than it is by using words instead of code :-)


                                    @RequestScoped @Named
                                    public class AddCategoryAction {
                                         @Produces @Any @Named Category category = new Category();
                                         @Inject List<Category> categories;
                                         
                                         public void execute() {
                                              categories.add(category);
                                         }
                                    }



                                    @ApplicationScoped
                                    public class CategoryList {
                                         @Produces @Named List<Category> categories = new ArrayList<Category>();
                                    }



                                    And in the JSF page you can now use #{category}, #{categories} and #{addCategoryAction.execute}.


                                    Frankly, I think this is nice :-)



                                    Or, alternatively:


                                    class Resources {
                                         @Produces @PersistenceContext EntityManager em;
                                    }



                                    @RequestScoped @Named
                                    public class AddCategoryAction {
                                         @Produces @Any @Named Category category = new Category();
                                         @Inject EntityManager em;
                                         
                                         public void execute() {
                                              em.persist(category);
                                         }
                                    
                                    }


                                    1 2 Previous Next