On Nov 14, 2008, at 11:58 AM, Gavin King wrote:
I've been thinking about the problem of implementing re-usable
components like our JMS endpoints. What I'm trying to do is implement
the requirements of JMS endpoints as a simple Web Bean.
Sounds good. The <Topic> and <Queue> special cases are strange. I
think, though, that the current JMS could be simplified tremendously
by using a special pseudo-scope on the QueueEndpoint.
Because the QueueEndpoint is a collection of related objects with a
common lifecycle, it makes sense to model it as a simple bean with the
proper scope and the @Produces as accessors to the objects. The scope
of the QueueEndpoint is dependent on the bean it's is injecting into.
When multiple @Produces are used by the target bean, the same
QueueEndpoint is used as a factory object.
So an injection of MyBean:
public class MyBean {
@PaymentProcessor QueueSender _paymentSender;
@PaymentProcessor QueueSession _paymentSession;
}
Would look like:
1. endpoint = new QueueEndpoint(); // triggered by first injection
2. endpoint.init();
3. endpoint.createQueueSender(); // first injection
4. endpoint.createQueueSession(); // endpoint grabbed from pseudo-
scope for 2nd injection
...
5. endpoint.destroy();
I don't see any need for a proxy at all in this case. Unless there's
something I'm missing, the only issue is the scope of the factory
object.
@DependentFactoryScoped
public class QueueEndpoint {
private Queue _queue;
private QueueConnection _conn;
private QueueSession _queueSession;
....
@PostConstruct
public void init()
{
// do any JNDI lookup, session creation, etc. (or do lazily)
}
@Produces
public Queue createQueue() { return _queue; }
@Produces
public QueueSession createQueueSession() { return _queueSession; }
...
@PreDestroy
public void destroy()
{
// shutdown
}
}
-- Scott
To do that
today, I would need to create a class which implemented all the API
types:
@ApplicationScoped
public class QueueEndpoint
implements Queue, QueueSession, QueueSender, QueueConnection
{ ... }
and I would have to go and implement every one of the operations of
each of those interfaces to delegate to the correct object. (A bunch
of tedious code.)
However, if we assume that the client proxy is smart enough, we could
let the Web Bean manager automagically do that delegation for us. We
would need a new annotation that could be applied to fields or
methods, for example:
@ApplicationScoped
public class QueueEndpoint {
...
@Supports Queue getQueue() { ... }
@Supports QueueSession getQueueSession() { ... }
@Supports QueueSender getQueueSender() { ... }
@Supports QueueConnection getQueueConnection() { ... }
}
We no longer need to write all those annoying delegation methods! The
client proxy would just call the correct @Supports method when the
QueueEndpoint type did not define the method that was invoked by the
client, and delegate the method to the returned object.
To make this really work, we would need to say:
* the set of API types of the Web Bean includes all types of @Supports
methods/fields
* if a certain method name appears on more than one @Supports
method/field type, it must also be implemented directly by the impl
class
* if a certain method name appears on more than one @Supports
method/field type, or on both the impl class and a @Supports
method/field type, it is always called on the impl class
Therefore, close() would need to be implemented by QueueEndpoint:
@ApplicationScoped
public class QueueEndpoint {
...
@Supports Queue getQueue() { ... }
@Supports QueueSession getQueueSession() { ... }
@Supports QueueSender getQueueSender() { ... }
@Supports QueueConnection getQueueConnection() { ... }
void close() { throw new UnsupportedOperationException(); }
}
You're probably thinking that we can do all this with @Producer
methods, however, the semantics are not exactly the same, since the
association b/w the producer object and produced object is lost as
soon as the producer method returns. Furthermore, @Producer methods
aren't really appropriate for 3rd-party reusable objects, since the
XML configuration of a producer method is verbose.
I don't believe that Web Beans 1.0 absolutely has to have this
feature, but I know its useful, and I know that we will implement it
in the RI, so I would prefer if it was portable. What do you guys
think? Useful? Too much magic? Let me know...
Seam already has an extremely primitive/braindead version of this
feature, that I've found *extremely* useful when writing reusable
components:
http://docs.jboss.com/seam/2.1.0.SP1/reference/en-US/html/concepts.html#d...
(@Factory is more or less like @Produces, whereas @Unwrap is a really
crappy version of @Supports.)
--
Gavin King
gavin.king(a)gmail.com
http://in.relation.to/Bloggers/Gavin
http://hibernate.org
http://seamframework.org