7 Replies Latest reply on Aug 16, 2006 11:40 AM by texan

    Injection woes

    texan

      How about adding a "@Singleton" annotation to Seam?

      Right now, I use this pattern:

      @In(create = true)
       @Out(scope = APPLICATION)
       private SomeClass myClass;


      Obviously, the target class itself isn't implemented as a singleton in this case, since it needs a public constructor.

      My point is, why perform an "outjection" every time when I'll only ever create it once? That is, the "@Out" (and create=true) is only truly needed once. I have no interest in reassigning "myClass" to a new instance and have that one pushed into the context. In fact, I'd like an error if I tried, preferably even a compiler error. [side note, can an annotation cause an instance variable to be redefined as "final?"]

      I'm being picky, but it would be nice to be able to do this:

      @Singleton
       private SomeClass myClass;


      or even

      @Singleton (method="getInstance")
       private SomeClass myClass;


      or possibly

      @Singleton
       private final SomeClass myClass;

      (if the annotation itself can't enforce the "final" notion)

      I would be quite content to do this myself, if I knew how. I'm assuming that, in addition to creating the annotation interface, I'd need to create an interceptor that is always called when my session beans are instantiated, and that interceptor would need to scan the member variables for this annotation and do the work of checking for an existing instance in the app context, etc.

      Presumably it would use the fully qualified class name as the context key rather than the instance variable name.

      Sadly, I haven't delved into interceptor creation just yet... Besides, this seems (in my selfish opinion) to be a Seam-worthy annotation.


        • 1. Re: Injection woes
          ssilvert

          If it is in application scope then it isn't really a singleton. If it is in application scope that doesn't mean there is only one instance per web app. It just means that there is only one instance at a time. There is nothing to stop code from changing the value of "myClass" that is stored in application scope.

          What might be interesting is a new scope called SINGLETON. The rule would be that you can add to the SINGLETON scope but you can't remove or replace anything.

          So you would have:

          @In(create = true)
          @Out(scope = SINGLETON)
          private SomeClass myClass;


          Outjection would only happen if the myClass instance got created. Would that satisfy your use case?

          Stan

          • 2. Re: Injection woes
            texan

            It would get the job done, but my goal also was to reduce it to one annotation, since @Singleton really says it all. :-)

            BTW, I had a terrible time with the following pattern today when referencing a simple java object that doesn't have a Seam @Name tag:

            @In (create = true)
            @Out (scope = SESSION)
            private DocumentManager docMgr;


            I'm not including the DocumentManager code, as it's just a java class with a simple public constructor.

            I kept getting the error where it was complaining about the docMgr attribute being required but not being initialized (I don't have the log file handy right now). Anyway, the only way I could make it work was to add an @Name tag to the DocumentManager class. This was even more frustrating because directly beneath those lines was the following block of code:

            @In (create = true)
            @Out (scope = SESSION)
            private QCCache qcCache;


            And this worked fine without any changes! (And QCCache has no @Name annotation either). Very odd that it worked for one case and not another...

            I was sort of assuming that Seam would be following the "use an intuitive default" approach and would use the variable's name as the context key, without having to instrument the referenced object. After all, I might want to reference a java.util.Date object, and I can't add a @Name annotaton to that without subclassing it.

            Any ideas? Am I just abusing the bijection feature?


            • 3. Re: Injection woes
              pmuir

              AFAIK POJOs without a @Name annotation cannot be injected. I'm surprised it worked for qcCache. I would suggest you look at @Unwrap and the component mananger pattern which allows Seam to manage the lifecycle of a non-seam component (which is how quite a lot of the seam core components work).

              So yes, I would say you are abusing bijection :p

              • 4. Re: Injection woes
                ssilvert

                 

                "ptmain" wrote:
                It would get the job done, but my goal also was to reduce it to one annotation, since @Singleton really says it all. :-)


                I don't think @Singleton really says it all. There is a difference between:

                Inject from any scope, create if needed, and outject to SINGLETON scope:
                @In (create = true)
                @Out (scope = SINGLETON)
                private DocumentManager docMgr;


                Inject from any scope, outject to SINGLETON scope:
                @In
                @Out (scope = SINGLETON)
                private DocumentManager docMgr;


                Inject from any scope, one of which could be SINGLETON
                @In
                private DocumentManager docMgr;


                Don't inject, but outject to SINGLETON scope
                @Out (scope = SINGLETON)
                private DocumentManager docMgr;


                I don't think this can/should be reduced to one annotation. Just my humble opinion.

                The more I think about it, the more I like the idea of a SINGLETON scope. Most of the time when you use application scope you really want a web app singleton. But application scope allows the singleton instance to be replaced or removed which could be a source of errors.

                Stan

                • 5. Re: Injection woes
                  texan

                  I really have gone through the Seam Reference, but the @Unwrap and @Factory methods still confuse me. I'm just not clear on what they do. I think that @Factory is just an initializer method so that the first time an instance variable is accessed and is null, if there is a @Factory method it will be called. (is that right?)

                  @Unwrap baffles me, though. The description in the reference implies that if I have an @Unwrap method in a BlogService component named "blog", then whenever I reference "blog" I'm actually getting an instance of a Blog instead (I'm using an example from the tutorial). In a later example, the @Unwrap method returns a list of BlogEntity objects, which is even stranger.

                  Tell me if I have this right: the "component manager pattern" is being used in the Blog example as a way to magically initialize data without explicitly doing so. So, if I reference the component "blog", the BlogServer actually fetches the "real" component from the database and that is what is returned. Similarly with the search: rather than the client getting a reference to the SearchService, it's actually getting a reference to the search results List.

                  Hmm, I think I just answered all of my questions about that.

                  In my original example, I think I will edit those classes that are within my project to make them Seam objects (@Name) and just move on with my life, and those outside of my project I may just subclass since they're not entity beans.

                  Or maybe I'll give up on my quest for the @Singleton annotation (which I still say would be really useful) and just use MyClass.getInstance().whatever(). I was mostly trying to see whether the injection hammer would be a good tool for banging the singleton nail (which turned out to be a staple and was just mangled).

                  However, the @Unwrap annotation is pretty slick! Somehow I passed right over it on my initial pass through the tutorial.

                  • 6. Re: Injection woes
                    pmuir

                     

                    @Name("myComponent")
                    public class MyComponentManager {
                    
                     private MyComponent myComponent;
                    
                     public MyComponentManager() {
                     myComponent = MyComponentFactory.getInstance();
                     }
                    
                     @Unwrap
                     public MyComponent getMyComponent() {
                     return myComponent;
                     }
                    


                    ...
                    @In(create=true)
                    private MyComponent myComponent;
                    ...
                    


                    Where MyComponent is a third-party non-seam-managed interface (e.g. something from your reporting library) and MyComponentFactory retrives an implementation for MyComponent.

                    HTH

                    • 7. Re: Injection woes
                      texan

                      Looks pretty straightforward.

                      Thanks!