struts-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Lukasz Lenart <lukaszlen...@apache.org>
Subject Re: Can we use the decorator pattern in Actions?
Date Thu, 04 Oct 2012 06:05:15 GMT
2012/10/3 Miguel Almeida <miguel@almeida.at>:
> I was speaking with Lukasz today about this, so I'm resurrecting this
> old thread.
>
> The underlying question in my (rather extensive) post is:
>
> How can you perform the following decorator pattern:
>
> public OriginalAction implements Preparable,
> SessionAware,OriginalActionInterface{
>
>   public String someFunctionality(){
>     ....
>   }
> }
>
> Decorate like:
>
> public DecoratedAction implements Preparable, SessionAware,etc{
>   private OriginalActionInterface originalAction; //inject
> OriginalAction here
>   @Secured
>   public String someFunctionality(){
>     // do new stuff
>     orignalAction.someFunctionality();
>   }
> }
>
> Issues:
> 1) Your OriginalAction will probably rely on some objects injected by
> struts (eg: session will probably be used). However, because
> OriginalAction is now only decorating DecoratedAction...those objects
> won't be automatically populated by Struts.
>
>
> The only way I see it is to use Spring IoC to define these needed
> objects in OriginalAction. But it would be neat if that was performed by
> Struts.
>
> What are your thoughts on this?
>
>
> Miguel Almeida
>
>
>  On Wed, 2012-05-16 at 11:22 +0100, Miguel Almeida wrote:
>
>> Imagine the scenario where you have security implemented at the action
>> method level with an annotation:
>>
>> @Secured("someRole") restricts that action to that role (and it is
>> checked with an interceptor).
>>
>> Discussing this on the TDD mailing list a while back, a decorator
>> approach was suggested
>>
>> To separate concerns and ease up testing, authorization is implemented
>> as a decorator (a-la GoF decorator pattern) adding authorization to the
>> underlying (decorated) MVC app.
>>
>> The technique to getting there (see pseudo code in [1])
>> 1. Extract an interface from your main class with all the public methods
>> 2. Implement a decorator which adds authorization rules to a decorated
>> underlying object. The decorator implements the authorization rules
>> using annotations
>> 3. in your tests, test the decorator providing a mock underlying
>> decorated object, asserting in each test that given a request with a
>> user that has certain roles the underlying method should or should not
>> be called.
>>
>>
>> As you see, tests would have a simple setup as you wouldn't be calling
>> "the real, possible complicated action code", but the fake decorated
>> one.
>> The problem arises when you have superclasses (and maybe also when you
>> implement interfaces). I exemplify with both.
>>
>> Imagine this:
>> RealAction extends CommonAction implements IAction(){
>> ...}
>>
>> CommonAction implements ServletRequestAware(){
>> ...
>> }
>>
>> AuthorizingDecorator implements IAction(){
>> //injected decorated IAction, see [1]
>> ...
>> }
>>
>> Now, on a regular RealAction implementation, the request object exists:
>> RealAction extends CommonAction, so the request is injected through its
>> ServletRequestAware.
>> If you're using the AuthorizingDecorator, however, the request will be
>> null: RealAction will be injected, so Struts won't kick in to populate
>> RealAction's request object.
>>
>>
>> My question is: how would you go on and solve this? Or is the decorator
>> approach impractical in Struts? I haven't even consider the necessity to
>> implement every getter/setter on the IAction, which would also make this
>> approach a bit cumbersome. The simplicity for testing, however, is
>> great!
>>
>> Cheers,
>>
>> Miguel Almeida
>>
>>
>> [1] The code might end up like this (semi-pseudo code)
>>
>> Tests:
>>
>> test_admin_can_call_method_a()
>>
>>         {
>>         // setup a fake request with admin role:
>>         httpRequest = buildRequestWithRole("admin");
>>
>>         // setup a mock app decorated with an authorzation decorator:
>>         MockApp app = new MockApp();
>>         AuthorizationDecorator authorizer = new
>>         AuthorizationDecorator(app);
>>
>>         // act - try calling method A in the decorator:
>>         authorizer.MethodA(httpRequest);
>>
>>         // assert - underlaying method a should have been called:
>>         Assert(app.MethodA.WasCalled==true);
>>
>> }
>>
>> test_regularUser_cannot_call_method_a()
>> {
>>
>>         // setup a fake request with regular user role:
>>         httpRequest = buildRequestWithRole("regular user");
>>
>>         // setup a mock app decorated with an authorzation decorator:
>>         MockApp app = new MockApp();
>>         AuthorizationDecorator authorizer = new
>>         AuthorizationDecorator(app);
>>
>>         // act - try calling method A in the decorator:
>>         authorizer.MethodA(httpRequest);
>>
>>         // assert - underlaying method a should not have been called:
>>         Assert(app.MethodA.WasCalled==false);
>>
>> }
>>
>> In the SUT:
>>
>> interface IAction
>> {
>>
>>         String MethodA()
>>         String MethodB()
>>         ...
>>
>> }
>>
>> // this is the real action implementing methodA, methodB etc
>> class RealAction: IAction
>> {
>>
>>         String MethodA()
>>         String MethodB()
>>         ...
>>
>> }
>>
>> // this is responsible for authorization
>> class AuthorizingDecoratorAction : IAction
>> {
>>
>>         private IAction _decorated;
>>         public AuthorizationDecorator(IAction decorated)
>>         {
>>         _decorated = decorated;
>>         }
>>
>>         // each method is implemented using annotations and calling the
>>         underlying decorated object
>>         @SecuredRoles("admin, manager")
>>         public void MethodA()
>>         {
>>         _decorated.MethodA();
>>         }
>>
>>         @SecuredRoles("regular user")
>>         public void MethodB()
>>         {
>>         _decorated.MethodB();
>>         }
>>
>> }
>
>

I thought about that a bit, but as I'm not sure what's the real use
case is so my proposal can be wrong ;-)
There are two option (both to extend struts2 itself), beside that you
can always write your own interceptor.

First is to add additional interface (ActionDecortator) that will mark
an action as a decorating action the original one and all the
injections / method calls will be performed onto
ActionDecorator#getDelegate(). Thus must be implemented in each
interceptor which interacts with action base on interface (Preparable,
ValidationAware, SessionAware, etc).

The second option is to add additional interface / annotation just for
selected existing interceptors, eg. ServletConfigInterceptor and new
ServletConfigDecorator#getDelegate(). Thus must be only implemented
with given interceptor.

We can always mix the both ways and start with one interface
(ActionDecorator) and add the functionality to all the interceptors
step by step.


Regards
-- 
Ɓukasz
+ 48 606 323 122 http://www.lenart.org.pl/

---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@struts.apache.org
For additional commands, e-mail: user-help@struts.apache.org


Mime
View raw message