Hi David,
Thanks for the detailed review. Gave me a lot of
insight into the spec. I have added my comments inline below. I guess
it is becoming tedious to read the inline comments, but please bear
with me :)
On 11/26/06, David Blevins <[EMAIL PROTECTED]> wrote:
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.
This is given in 3.6.1. Probably this is the default way of specifying
names as the name attribute of the @EJB annotation is not mandatory.
>
> 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.
Yes I missed this. Mohammad was kind enough to correct me and point me
to the spec where the above is given.
> 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?
I just searched the Web Services for Java EE, Version 1.2 spec (did
not read it completely) and found this usage in Section 4.2.2
> 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.
What we meant by this note is that in JNDI the resource will be bound
to java:comp/env/
context and not to the recommended subcontexts by default
> 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.
One question on this, How are different states maintained by the
single instance if the client modifies the state?
> 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 :)
This is given in the EJB Core spec section 16.2.2
> 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?
If we do not support this and we have a subclassed bean with inherited
methods with annotations used for DI that are not overridden, how will
the values be injected for those methods? Even the private methods
which may not be visible may be used by some visible method that was
not overridden.
>
> 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?
Great Idea!!! Would reduce a lot of work.
> ----------------------------------------------------------------------
> ------------------------------------------------------------
>
> 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
>> >
>> >
>>
>
Thanks
Manu