You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Paul Field <pa...@db.com> on 2009/09/24 22:29:12 UTC

Thoughts on an equivalent of "prototype" beans in Tapestry IOC

There was a recent thread that discussed implementing the equivalent of 
Spring's "prototype" beans in Tapestry IOC. I was of the opinion that it 
wasn't possible to implement sensibly: you either run the risk of the 
service living too long or else Tapestry's proxies would mean that you 
created a new instance for every method call.

I've been thinking about this some more, and I've realised that the 
problem is about controlling the scope. Prototype beans in Spring are a 
bit problematic anyway and the reason is that the scope is started by a 
request to the container for the bean and then ended by the last reference 
being removed (GC).  Sometimes the "call to the container" is hidden - so 
it's not very clear exactly what the scope is and that would be 
particularly true in Tapestry where the proxies and @Inject mechanisms 
hide the container well away from the client code.

So I think that the key to doing a similar thing in Tapestry IOC is to 
make control of the scope explicit. This would be known as "manual" scope 
and the pattern would be something like this:


-----------------
// This is provided by a hypothetical library and is actually 'perthread' 
scoped - so your manual scope is nested within a thread
public interface ManualScope {
  // Opens the scope - error if already open
  void open();

  // Closes the scope - error if not open
  void close();

  // Runs 'r' inside the scope - error if scope is already open
  void run(Runnable r);
}

-----------------
// This is your module creating your manually-scoped service (somewhat 
equivalent to a spring prototype bean)
public class MyModule {
    public static void bind(ServiceBinder binder) {
        binder.bind(MyService.class, MyServiceImpl.class).scope("manual");
    }
}

-----------------
// This is your client code using the service

@Inject ManualScope manualScope;
@Inject MyService myManualScopedService;


public void doSomethingOldSchool() {
    manualScope.open()
    try {
        myManualScopedService.doSomething();
        myManualScopedService.doSomeMore();
    } finally {
        manualScope.close();
    }
}


public void doSomethingNewSchool() {
    manualScope.run(new Runnable() {
        public void run() {
            myManualScopedService.doSomething();
            myManualScopedService.doSomeMore();
        }
    });
}


So the idea is that you can only use myManualScoped bean within the 
open()/close() pair, or within the run(). If you use it outside, you get a 
helpful exception. 


There other options for 'prototype' beans I can think of involve not 
having a service. Either:
1) Create a factory service; the client code injects the factory and calls 
a create() method.
2) Inject ObjectLocator and call autobuild()  (gives you the benefit of 
dependency injection for a non-service object)

Both of these are much simpler for the client. The manual scope is a bit 
clunky to use. The main benefit is that the service is a proper IOC 
service you can decorate it/advise it/override it and all kinds of other 
good IOC stuff. In addition, you have complete control over the lifetime 
of your service and potentially there could be a 
ManualScopeCleanupListener (similar to ThreadCleanupListener) to clean-up 
services when the scope closes.

I'm pretty sure my work project would find it useful as, in Spring, I have 
one-shot "commands" that are prototype beans and we use Spring's AOP to 
add-on transactions, logging and auditing. This Manual scope idea would 
work quite well for that as we could use the IOC's decorating/advising 
capabilities.


However, I'm not convinced that the clunkyness of the client code won't 
put people off using it and I'm not convinced that there are enough times 
that you want to use IOC magic (such as advice) on a "prototype bean". So, 
I'd appreciate some feedback on the idea.

Can you think of any simplification I could make that makes it less 
clunky? Would you use it? Is it useful enough for me to write the 
implementation as open source? Useful enough to pester the committers to 
put it in the core IOC? Or just a bad idea that I've had late one evening 
:-)

Thanks,

- Paul



---

This e-mail may contain confidential and/or privileged information. If you are not the intended recipient (or have received this e-mail in error) please notify the sender immediately and delete this e-mail. Any unauthorized copying, disclosure or distribution of the material in this e-mail is strictly forbidden.

Please refer to http://www.db.com/en/content/eu_disclosures.htm for additional EU corporate and regulatory disclosures.

Re: Thoughts on an equivalent of "prototype" beans in Tapestry IOC

Posted by Paul Field <pa...@db.com>.
Howard Lewis Ship <hl...@gmail.com> wrote on 24/09/2009 21:34:06:
> What about nestable manual scopes?

I was thinking of not allowing them because I wanted to give errors if you 
got the open()/close() mismatched. But actually, now I think about it, the 
manual scope is nested in a perthread scope, so I could warn about bad 
nesting when the thread cleans up, so nested scopes would be reasonable.

-Paul



> On Thu, Sep 24, 2009 at 1:29 PM, Paul Field <pa...@db.com> wrote:
> 
> > There was a recent thread that discussed implementing the equivalent 
of
> > Spring's "prototype" beans in Tapestry IOC. I was of the opinion that 
it
> > wasn't possible to implement sensibly: you either run the risk of the
> > service living too long or else Tapestry's proxies would mean that you
> > created a new instance for every method call.
> >
> > I've been thinking about this some more, and I've realised that the
> > problem is about controlling the scope. Prototype beans in Spring are 
a
> > bit problematic anyway and the reason is that the scope is started by 
a
> > request to the container for the bean and then ended by the last 
reference
> > being removed (GC).  Sometimes the "call to the container" is hidden - 
so
> > it's not very clear exactly what the scope is and that would be
> > particularly true in Tapestry where the proxies and @Inject mechanisms
> > hide the container well away from the client code.
> >
> > So I think that the key to doing a similar thing in Tapestry IOC is to
> > make control of the scope explicit. This would be known as "manual" 
scope
> > and the pattern would be something like this:
> >
> >
> > -----------------
> > // This is provided by a hypothetical library and is actually 
'perthread'
> > scoped - so your manual scope is nested within a thread
> > public interface ManualScope {
> >  // Opens the scope - error if already open
> >  void open();
> >
> >  // Closes the scope - error if not open
> >  void close();
> >
> >  // Runs 'r' inside the scope - error if scope is already open
> >  void run(Runnable r);
> > }
> >
> > -----------------
> > // This is your module creating your manually-scoped service (somewhat
> > equivalent to a spring prototype bean)
> > public class MyModule {
> >    public static void bind(ServiceBinder binder) {
> >        binder.bind(MyService.class, 
MyServiceImpl.class).scope("manual");
> >    }
> > }
> >
> > -----------------
> > // This is your client code using the service
> >
> > @Inject ManualScope manualScope;
> > @Inject MyService myManualScopedService;
> >
> >
> > public void doSomethingOldSchool() {
> >    manualScope.open()
> >    try {
> >        myManualScopedService.doSomething();
> >        myManualScopedService.doSomeMore();
> >    } finally {
> >        manualScope.close();
> >    }
> > }
> >
> >
> > public void doSomethingNewSchool() {
> >    manualScope.run(new Runnable() {
> >        public void run() {
> >            myManualScopedService.doSomething();
> >            myManualScopedService.doSomeMore();
> >        }
> >    });
> > }
> >
> >
> > So the idea is that you can only use myManualScoped bean within the
> > open()/close() pair, or within the run(). If you use it outside, you 
get a
> > helpful exception.
> >
> >
> > There other options for 'prototype' beans I can think of involve not
> > having a service. Either:
> > 1) Create a factory service; the client code injects the factory and 
calls
> > a create() method.
> > 2) Inject ObjectLocator and call autobuild()  (gives you the benefit 
of
> > dependency injection for a non-service object)
> >
> > Both of these are much simpler for the client. The manual scope is a 
bit
> > clunky to use. The main benefit is that the service is a proper IOC
> > service you can decorate it/advise it/override it and all kinds of 
other
> > good IOC stuff. In addition, you have complete control over the 
lifetime
> > of your service and potentially there could be a
> > ManualScopeCleanupListener (similar to ThreadCleanupListener) to 
clean-up
> > services when the scope closes.
> >
> > I'm pretty sure my work project would find it useful as, in Spring, I 
have
> > one-shot "commands" that are prototype beans and we use Spring's AOP 
to
> > add-on transactions, logging and auditing. This Manual scope idea 
would
> > work quite well for that as we could use the IOC's decorating/advising
> > capabilities.
> >
> >
> > However, I'm not convinced that the clunkyness of the client code 
won't
> > put people off using it and I'm not convinced that there are enough 
times
> > that you want to use IOC magic (such as advice) on a "prototype bean". 
So,
> > I'd appreciate some feedback on the idea.
> >
> > Can you think of any simplification I could make that makes it less
> > clunky? Would you use it? Is it useful enough for me to write the
> > implementation as open source? Useful enough to pester the committers 
to
> > put it in the core IOC? Or just a bad idea that I've had late one 
evening
> > :-)
> >
> > Thanks,
> >
> > - Paul
> >
> >
> >
> > ---
> >
> > This e-mail may contain confidential and/or privileged information. If 
you
> > are not the intended recipient (or have received this e-mail in error)
> > please notify the sender immediately and delete this e-mail. Any
> > unauthorized copying, disclosure or distribution of the material in 
this
> > e-mail is strictly forbidden.
> >
> > Please refer to http://www.db.com/en/content/eu_disclosures.htm for
> > additional EU corporate and regulatory disclosures.
> 
> 
> 
> 
> -- 
> Howard M. Lewis Ship
> 
> Creator of Apache Tapestry
> 
> The source for Tapestry training, mentoring and support. Contact me to 
learn
> how I can get you up and productive in Tapestry fast!
> 
> (971) 678-5210
> http://howardlewisship.com



---

This e-mail may contain confidential and/or privileged information. If you are not the intended recipient (or have received this e-mail in error) please notify the sender immediately and delete this e-mail. Any unauthorized copying, disclosure or distribution of the material in this e-mail is strictly forbidden.

Please refer to http://www.db.com/en/content/eu_disclosures.htm for additional EU corporate and regulatory disclosures.

Re: Thoughts on an equivalent of "prototype" beans in Tapestry IOC

Posted by Howard Lewis Ship <hl...@gmail.com>.
What about nestable manual scopes?

On Thu, Sep 24, 2009 at 1:29 PM, Paul Field <pa...@db.com> wrote:

> There was a recent thread that discussed implementing the equivalent of
> Spring's "prototype" beans in Tapestry IOC. I was of the opinion that it
> wasn't possible to implement sensibly: you either run the risk of the
> service living too long or else Tapestry's proxies would mean that you
> created a new instance for every method call.
>
> I've been thinking about this some more, and I've realised that the
> problem is about controlling the scope. Prototype beans in Spring are a
> bit problematic anyway and the reason is that the scope is started by a
> request to the container for the bean and then ended by the last reference
> being removed (GC).  Sometimes the "call to the container" is hidden - so
> it's not very clear exactly what the scope is and that would be
> particularly true in Tapestry where the proxies and @Inject mechanisms
> hide the container well away from the client code.
>
> So I think that the key to doing a similar thing in Tapestry IOC is to
> make control of the scope explicit. This would be known as "manual" scope
> and the pattern would be something like this:
>
>
> -----------------
> // This is provided by a hypothetical library and is actually 'perthread'
> scoped - so your manual scope is nested within a thread
> public interface ManualScope {
>  // Opens the scope - error if already open
>  void open();
>
>  // Closes the scope - error if not open
>  void close();
>
>  // Runs 'r' inside the scope - error if scope is already open
>  void run(Runnable r);
> }
>
> -----------------
> // This is your module creating your manually-scoped service (somewhat
> equivalent to a spring prototype bean)
> public class MyModule {
>    public static void bind(ServiceBinder binder) {
>        binder.bind(MyService.class, MyServiceImpl.class).scope("manual");
>    }
> }
>
> -----------------
> // This is your client code using the service
>
> @Inject ManualScope manualScope;
> @Inject MyService myManualScopedService;
>
>
> public void doSomethingOldSchool() {
>    manualScope.open()
>    try {
>        myManualScopedService.doSomething();
>        myManualScopedService.doSomeMore();
>    } finally {
>        manualScope.close();
>    }
> }
>
>
> public void doSomethingNewSchool() {
>    manualScope.run(new Runnable() {
>        public void run() {
>            myManualScopedService.doSomething();
>            myManualScopedService.doSomeMore();
>        }
>    });
> }
>
>
> So the idea is that you can only use myManualScoped bean within the
> open()/close() pair, or within the run(). If you use it outside, you get a
> helpful exception.
>
>
> There other options for 'prototype' beans I can think of involve not
> having a service. Either:
> 1) Create a factory service; the client code injects the factory and calls
> a create() method.
> 2) Inject ObjectLocator and call autobuild()  (gives you the benefit of
> dependency injection for a non-service object)
>
> Both of these are much simpler for the client. The manual scope is a bit
> clunky to use. The main benefit is that the service is a proper IOC
> service you can decorate it/advise it/override it and all kinds of other
> good IOC stuff. In addition, you have complete control over the lifetime
> of your service and potentially there could be a
> ManualScopeCleanupListener (similar to ThreadCleanupListener) to clean-up
> services when the scope closes.
>
> I'm pretty sure my work project would find it useful as, in Spring, I have
> one-shot "commands" that are prototype beans and we use Spring's AOP to
> add-on transactions, logging and auditing. This Manual scope idea would
> work quite well for that as we could use the IOC's decorating/advising
> capabilities.
>
>
> However, I'm not convinced that the clunkyness of the client code won't
> put people off using it and I'm not convinced that there are enough times
> that you want to use IOC magic (such as advice) on a "prototype bean". So,
> I'd appreciate some feedback on the idea.
>
> Can you think of any simplification I could make that makes it less
> clunky? Would you use it? Is it useful enough for me to write the
> implementation as open source? Useful enough to pester the committers to
> put it in the core IOC? Or just a bad idea that I've had late one evening
> :-)
>
> Thanks,
>
> - Paul
>
>
>
> ---
>
> This e-mail may contain confidential and/or privileged information. If you
> are not the intended recipient (or have received this e-mail in error)
> please notify the sender immediately and delete this e-mail. Any
> unauthorized copying, disclosure or distribution of the material in this
> e-mail is strictly forbidden.
>
> Please refer to http://www.db.com/en/content/eu_disclosures.htm for
> additional EU corporate and regulatory disclosures.




-- 
Howard M. Lewis Ship

Creator of Apache Tapestry

The source for Tapestry training, mentoring and support. Contact me to learn
how I can get you up and productive in Tapestry fast!

(971) 678-5210
http://howardlewisship.com