Manu and Mohammad, this is excellent work!  Comments below...


On Nov 22, 2006, at 10:56 PM, Manu George wrote:

Dependency Injection
Every possible type of JNDI entry can be injected via fields/ annotations. Fields that need to have values injected should not be static or final.
If a session bean makes use of dependency injection, the container
injects these references after the bean instance is created, and
before any business methods are invoked on the bean instance. If
dependency injection fails, the bean instance is discarded. The
Deployer can mark fields for Dependency injection by using deployment
descriptor elements as well as annotations. Always deployment
descriptor elements override the annotations.Container must throw
NameNotFoundException if any reference is not found. But what if we
try
to inject? We are assuming that no value will be injected and no
exception will be thrown.

The NameNotFoundException likely refers to the fact that injection is really based on JNDI. All values, injected or not, are placed into JNDI. When injection occurs, the value is looked up from JNDI by the container and then injected into the bean instance. Hence NameNotFoundException if the value is not there. Granted you're not required to do it exactly like that, i.e. the container could have some other store for injected values it references rather than JNDI, but that's the way everyone on the Expert Group was imagining it would be implemented.

Regardless, this will never happen for us as we will strictly refuse to deploy a bean that references something via dependency injection which does not exist. Reason being is that with creates happening on injection and injection happening on create, at runtime we will be recursively creating objects to inject into other objects. A failed create somewhere in the chain will cascade all the way to the chain like dominoes and would be very hard to debug. We also need to make sure that no circular references occur -- though if we didn't use JNDI and could pass out references to partially initialized objects those could actually work.

Anyway, for the sake of a lot of simplicity, apps with circular or invalid references will fail validation and not be deployed. Along these lines though, we may want a vendor-specific @Optional tag which could hint is that it's not the end of the world of the reference doesn't resolve and that the bean instance could be constructed and used without it.

Field Injection

In the case of variables
@Resource(name="myDB") //type is inferred from variable
public DataSource customerDB;
If the variable name is myDB then the variable is
@Resource  //type and name are inferred from variable
public DataSource myDB;
@EJB //reference name and type inferred from variable
public AddressHome addressHome;

Looks good. Also field scopes of private, projected and "default" are allowed -- though the spec doesn't make that as clear as it could.

Setter Injection

@Resource(name="customerDB")
public void setDataSource(DataSource myDB) {
this.ds = myDB;
}
@Resource // reference name is inferred from the property name and
resource type                            //by parameter type
public void setCustomerDB(DataSource myDB) {
this.customerDB = myDB;
}
@Resource
public void setSessionContext(SessionContext ctx) {
this.ctx = ctx;
}

Also looks good. As above method scopes of private, projected and "default" are allowed

As a side note, though outside the spec, constructor injection is a feature we will want to support. We well want tests for this but we'll likely want to keep them somewhat separate from the other tests. Not something we need to create now, just keep it in the back of your minds.

Types of references in JNDI

1) Simple environment entries.
   For injecting simple environment entries use the annotation
   @Resource int maxExemptions;
   env-entry elements in the deployment descriptor.
   The environment entry values may be one of the following Java
types: String, Character,Integer, Boolean, Double, Byte, Short, Long,
   and Float.

Another note on a planned OpenEJB "vendor extension", we will be supporting a number of extra types such as URL, URI, Properties, BigDecimal, and more as well as allowing users to "plug-in" JavaBeans Property Editors for extending those types. We'll want to test that as well in some sort of separate test as noted above.

   Environment entries are scoped to a particular bean.
   Page 412 of the core spec shows how to specify injection of
   environment entries using the deployment descriptor. If
<env-entry-value> tag is not specified then the named resource is not initialized in the naming context, and explicit lookups of the named
   resource will fail.The container must only inject a value for the
   environment entry if the application assembler or deployer has
   specified a value to override the default value.

Looks good. Just as a note, we'll want to test the injection of values when no annotations and only the deployment descriptor is used.

2) EJB References
    a) EJB business interface
      A client can obtain a session bean's business interface through
dependency injection or lookup in the JNDI namespace.For example, the business interface Cart for the CartBean session bean may be obtained
      using dependency injection as follows: @EJB Cart cart;
      @EJB(
      name="ejb/shopping-cart",
      beanInterface=ShoppingCart.class,
      beanName="cart1",
      description="The shopping cart for this application"
      )
      private ShoppingCart myCart;

Good.

    b) Home interface for EJB 2.1 Beans
For example, an EJB 3.0 client, com.acme.example.MySessionBean, might
      obtain a reference to a bean's home interface as follows:
      @EJB CartHome cartHome;

      @EJB(
      name="ejb/shopping-cart",
      beanInterface=ShoppingCartHome.class,
      beanName="cart1",
      description="The shopping cart for this application"
      )
      private ShoppingCartHome myCartHome;

This home interface could be looked up in JNDI using the EJBContext
      lookup method as shown in the following code segment:
      @Resource SessionContext ctx;
      ...
      CartHome cartHome =
(CartHome)ctx.lookup("com.acme.example.MySessionBean/ cartHome");
      If all the information is not given then it becomes the
      deployer's responsibility to map the bean

I'm not sure what part of the spec would imply that particular JNDI name. The actual name for a context lookup should be the value of @EJB.name. For a jndi lookup it'd be the same but with "java:comp/ env/" prepended to it.


3) Web Service References
    WebService-Ref is the annotation,
      a) JAX-RPC Web Service Clients
          Dependency injection is not supported for these web
services.(Not sure abt this)

I haven't studied the JAX-WS spec too closely yet, so I can't say for sure. I looked around and don't see any real difference in how a service-ref element is treated for a JAX-WS service as opposed to a JAX-RPC service. It would appear that @WebServiceRef could be used equally well for both JAX-RPC and JAX-WS endpoints.

      b) JAX-WS Web Service

WebService-Ref is the annotation and it can be used to declare a
          service or a service endpoint
@WebServiceRef(name="java:comp/env/service/ AddressBookService")
            AddressBookService abf;

@WebServiceRef(name="java:comp/env/service/ AddressBookService",
            AddressBookService.class)
            AddressBookPort port;
          service-ref is deployment descriptor element

I don't think it's legal to put "java:comp/env/" in the name of @EJB, @Resource, @WebServiceRef, etc. Did you see this somewhere?

4) Resource Manager Connection Factory References
    Can be declared using annotations and container will inject.
    @Resource javax.sql.DataSource employeeAppDB;
    4 types of connection factories need to be tested
      a) JDBC
      b) JMS
      c) Mail
      d) URL
    Note that resource manager connection factory references
declared via annotations will not, by default, appear in any subcontext.

Do you have a spec reference for that note? Usages of @Resource on the class (as opposed to methods and fields) definitely result in additions to JNDI which can be looked up via JNDI or the EJBContext. For our impl we'll definitely add @Resource usages on methods and fields to JNDI.

    Resource injection into classes should also be tested.
      @Resource(name="jdbc/EmployeeAppDB", type=javax.sql.DataSource)
      @Stateless public class EmployeeServiceBean
      implements EmployeeService {
      @Resource SessionContext ctx;
      public void changePhoneNumber(...) {
      ...
      // use context lookup to obtain resource manager
      // connection factory
      javax.sql.DataSource ds = (javax.sql.DataSource)
      ctx.lookup("jdbc/EmployeeAppDB");
      // Invoke factory to obtain a connection. The security
      // principal is not given, and therefore
      // it will be configured by the Deployer.
      java.sql.Connection con = ds.getConnection();
      ...
      }
      }

5) Resource Environment References
    A field or a method of a bean may be annotated with the
    Resource annotation to request injection of a resource environment
    reference.

6) Message Destination References
    Injection of a Message Destination Reference
    @Resource javax.jms.Queue stockQueue;

    The following example illustrates how an enterprise bean uses a
    message destination reference to locate a JMS Destination.

     @Resource(name="jms/StockQueue", type=javax.jms.Queue)
@Stateless public class StockServiceBean implements StockService {
     @Resource SessionContext ctx;
     public void processStockInfo(...) {
     ...
     // Look up the JMS StockQueue in the environment.
     Object result = ctx.lookup("jms/StockQueue");
     // Convert the result to the proper type.
     javax.jms.Queue queue = (javax.jms.Queue)result;
     }
     }

 7) Persistence Unit References
     The field or method should be annotated with a PersistenceUnit
Annotation.The name element specifies the name under which the entity manager factory for the referenced persistence unit may be located in the JNDI naming context. The optional unitName element specifies the name of the persistence unit as declared in the persistence.xml file
     that defines the persistence unit.

       @PersistenceUnit
       EntityManagerFactory emf;

       @PersistenceUnit(unitName="InventoryManagement")
       EntityManagerFactory inventoryEMF;

       java:comp/env/persistence subcontext to be used

This is good. It is possible to have a persistence unit in "java:comp/env/InventoryManagement". Usage of "java:comp/env/ persistence" is only convention.

     Obtaining entity manager for a persistence context

       @PersistenceContext(name="persistence/InventoryAppMgr")
       @Stateless
       public class InventoryManagerBean implements InventoryManager {
       @Resource SessionContext ctx;
       public void updateInventory(...) {
       ...
       // use context lookup to obtain container-managed entity
       manager
       EntityManager em =(EntityManager)
       ctx.lookup("persistence/InventoryAppMgr");
      ...
      }
      }

    persistence-context-ref used to declare the persistence context
    references in the deployment descriptor.

 8) UserTransaction Interface
     Only Session and Message Driven beans with BMT can access the
     UserTransaction interface available at java:comp/UserTransaction.


       @Resource UserTransaction tx;
       ...
       public void updateData(...)
       ...
       // Start a transaction.
       tx.begin();
       ...
       // Perform transactional operations on data
       ...
       // Commit the transaction.
       tx.commit();
       ...
       }

Great.  This one is easy to miss.


 9) ORB References
     java:comp/ORB

       @Resource ORB orb;
       public void method(...) {
       ...
       // Get the POA to use when creating object references.
       POA rootPOA = (POA)orb.resolve_initial_references("RootPOA");
       ...
       }
     An ORB reference may also be declared in a deployment
descriptor in the same way as a resource manager connection factory reference. Such a deployment descriptor entry may be used to specify injection of an ORB object.The application may set the shareable element of the Resource annotation to false, or may set the res- sharing-scope
     element in the deployment descriptor to Unshareable, to request a
     non-shared ORB instance.

10) TimerService References

     The Timer Service is accessed via dependency injection(Resource
     annotation), through the getTimerService method of the EJBContext
     interface, or through lookup in the JNDI
     namespace(java:comp/TimerService).

11) EJBContext References
     Are made available through dependency injection(using the
     Resource Annotaton) or in JNDI (under the name
     java:comp/EJBContext).An EJBContext object reference may also be
     declared in a deployment descriptor in the same way as a resource
environment reference.Such a deployment descriptor entry may be used to specify injection of an EJBContext object.EJBContext objects accessed
     through the naming environment are only valid within the bean
instance that
performed the lookup. Lookup should give SessionContext for session
     beans and MessageDrivenContext for MDBs

     EJBContext.getEnvironment Method need not be supported and is
     deprecated. Runtime exception or subclass of it should be thrown

Right. As a note, we don't support EJBContext.getEnvironment (it's an old EJB 1.0 thing that was immediately deprecated)

       @Resource SessionContext ctx;
       ...
       Cart cart = (Cart)ctx.lookup(cart);
This should be irrespective of whether the interface is local or remote. Can be in another EJB or in a standalone clientIf a dependency on the
       SessionContext is declared, or if the bean class implements the
optional SessionBean interface (see Section 4.3.5), the SessionContext is also injected at this time. In the case of the SessionContext being acquired through dependency injection,the Resource annotation (or resource-env-ref deployment descriptor element) is used to denote the bean's dependency on the SessionContext. The SessionContext should be
       injected first before anything else.

Notes
1) In the JNDI namespace,every lookup should return a new object other
than for shared objects like ORB,singletons or immutable objects

Same goes for injection. On this note, I've been kicking around the idea of supporting the concept of shared Stateful beans. The idea basically being that anytime someone looks up or has injected a reference to the Stateful bean they all get a reference to the same instance rather than each getting a new instance. Obviously there'd be certain things that need to be worked out like, do we support remove, do we scope on a per client basis or per vm or even a per thread basis. Could be a cool programming construct for ejbs.

2) Fields that need to have values injected should not be static or final
3) JNDI name format -->
java:comp/env/com.acme.example.MySessionBean/myDatabase. -->
java:comp/env/package.Class/Field. The @Resource annotation also allows the
JNDI name to be specified explicitly.
4) Setter Injection for properties
setMyDatabase -> java:comp/env/com.example.MySessionBean/myDatabase.

Not sure where the package.Class thing is coming from. Could be something I've missed :)

5) When Deployment descriptor is used specify both JNDI name and property name
You can specify non default names to inject.
6) Each resource can be injected into a single field or method only
either the field or method can request the injection of a non default
named resource.By explicitly specifying the JNDI name of a resource, a
single resource may be injected into multiple fields or methods of
multiple classes.

7) Not visible/Hidden fields in the superclass (eg private fields) may
request injection as well. Also overridden methods will follow
whatever is given in that method.

Not sure how I feel about the whole concept of injecting up the inheritance chain from super class to super class on a bean instance. On one hand it's a bit more complicated to implement, on the other hand it could be pretty powerful construct for people (albeit possible complex). What are other people's thoughts here?


8) The container needs to prompt the Deployer of any unresolved EJB
references, and allow him or her to resolve an EJB reference by
binding it to a specified compatible target bean.

This is a requirement, but not something we need to test for.


We need to create test cases for each type of resource with both
annotations and dd based injection as well as overrides.

So I had some thoughts on the overriding concept. I was thinking one way we could efficiently test all the possible overrides would be to build up partial openejb-jee trees (the objects that represent the xml), feed them into the AnnotationDeployer where overriding happens and the defaults applied, then marshal the openejb-jee tree back out to an ejb-jar.xml and compare it against one we've pre-created that has the expected resulting tree. They'd be a bit like a cross between the JeeTest and StatelessContainerTest.

Thoughts?

---------------------------------------------------------------------- ------------------------------------------------------------

P.S. In all the test cases we test injection of only public fields but
the spec only says that we cannot inject into static and final fields.
David can you clarify why only public fields can be injected.

All throughout the spec development we were pretty adamant about only public fields being eligible for injection to ensure that all beans created could be easily unit tested outside the container -- i.e. the unit test could set all the fields or properties of the bean then test it's methods. The spec reads this way generally but at some point just before the spec was completed that was abandoned without much discussion and now private fields and methods are eligible for injection.

Anyway, very very excellent post. Will respond to the other posts tomorrow :)

-David


Thanks
Manu

On 11/16/06, Manu George <[EMAIL PROTECTED]> wrote:
Hi David and Mohammad,
                    I will be happy to take this up and collaborate
with Mohammad and anyone else interested. I will take up the public
field injection tests first

  [OPENEJB-161] iTest: StatelessBeanPublicFieldInjectionTests
  [OPENEJB-187] iTest: StatefulBeanPublicFieldInjectionTests
Will start combing through the spec :-) for related info

Thanks
Manu


On 11/16/06, Mohammad Nour El-Din <[EMAIL PROTECTED]> wrote:
> Hi David...
>
> The mail is not directed to me :), but I really want to join into these > tests specially the ones which their container code is not implemented yet > so I can write the test case and go through its related container code too
> :), so I would like to have these ones:
>
>  [OPENEJB-160] iTest: StatelessBeanSetterInjectionTests
>  [OPENEJB-186] iTest: StatefulBeanSetterInjectionTests
>
> On 11/15/06, David Blevins <[EMAIL PROTECTED]> wrote:
>
> > On Nov 9, 2006, at 6:10 AM, Manu George wrote:
> >
> > > Hi David,
> > > That explains it. The Pojo name in some of the test > > > cases. I was wondering what it was. I have attached the patches for
> > > 152 and 156.
> >
> > Ok, I've got 152 and 156 applied and checked in. Thank you very much
> > for those!
> >
> > > Please assign some others to me namely 154 and 155.
> >
> > I see you already have these completed and patches in for those --
> > you work fast.  I'll get those in for you today too.
> >
> > Thanks Manu! You're really cruising these tests. Any interest in
> > working on one of these?
> >
> >     [OPENEJB-160] iTest: StatelessBeanSetterInjectionTests
> >     [OPENEJB-161] iTest: StatelessBeanPublicFieldInjectionTests
> >     [OPENEJB-162] iTest: StatelessBeanCallbackTests
> >     [OPENEJB-186] iTest: StatefulBeanSetterInjectionTests
> >     [OPENEJB-187] iTest: StatefulBeanPublicFieldInjectionTests
> >     [OPENEJB-188] iTest: StatefulBeanCallbackTests
> >
> > Not as easy as the other ones as there's no pre-existing test to copy/ > > paste from. But I get the feeling you may be up to the challenge. > > The corresponding setter injection code in the container has not been
> > written, so these tests will not pass but they will be a very big
> > help in completing that functionality.
> >
> > Thoughts?
> >
> > -David
> >
> >
>
>
> --
> Thanks
> - Mohammad Nour
>
>



Reply via email to