Announcement Announcement Module
Collapse
No announcement yet.
Context Aware Commands Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Context Aware Commands

    I'm often finding that my commands need to be aware of their context. As it is now, a single ActionCommand can be used to create buttons that execute the command. It would be useful for me to create multiple buttons that map to the same ActionCommand. A general purpose command could have buttons in several different views or locations in the same "page". When the command is executed, it would be extremely useful to know what view/page/dialog/whatever the command was executed in. Maybe there is an elegant way to solve this problem with spring-rich as it is now?
    What I'm doing now to deal with this is to create new instances of the command for each view (or location) and then set properties on each particular instance telling it where it is. The problem with this approach is that it forces me to either: 1) create my own instance manually and do manual IoC, bypassing all the wonderfulness of Spring, or 2) make the command a non-singleton in the Spring config. Option 1 is ugly and a pain, while option 2 has pitfalls associated with it. Besides, even with option 2, I still have to do some manual IoC to set the contextual properties.
    What would be the best approach to solving this issue?

    Thanks,
    Andy

  • #2
    Just had a thought - what about adding a createButton method to AbstractCommand that will pass a set of parameters to the ParameterizedActionCommandExecutor.execute(paramet ers) method when the button is clicked? In my view creating code, I could do something like this:
    Code:
    Map params = new HashMap();
    params.put("view", this);
    AbstractButton button = command.createButton(params);
    panel.add(button);
    Whenever the button is pressed, the "view" parameter will be set to its proper value when the command is executed.

    - Andy

    Comment


    • #3
      Andy,

      The way I recommend handling context is to use a TargetableActionCommand --- you can retarget the command by changing the executor, essentially changing the context on what is executed (and on what and when, for example.)

      That's exactly how the applicaiton managemetn abstractions work for attaching local executors to shared commands.

      Comment


      • #4
        I had thought of this, but unless I'm mistaken, I don't think it will work with what I'm saying. Basically, I'm using a single ActionCommand instance to generate multiple buttons. These multiple buttons will be visible simultaneously on the screen. Each button maps to the same action, only with different context. From what I understand, TargetableActionCommand allows me to retarget an ActionCommand instance to a single executor. This allows me the flexibility to have a single visible button that can map to multiple executors based on context - which is not what I need in this case (it rules out having multiple buttons that execute the same action but with different context). If I used a TargetableActionCommand to generate multiple buttons, I would end up with multiple buttons pointing to the same action and target pairing - and I end up with the same problem I started with.
        Any ideas? If this doesn't make sense, let me know and I'll try to give you an example.

        - Andy

        Comment


        • #5
          A Suggestion

          Keith:

          Could you not:

          (a) add an ActionRequest class;
          (b) add an ActionResponse class;
          (c) overload doExecuteCommand() to accept a request and a response object and return a response object.

          That should make the Spring RCP Action/Command Framework very versitile.
          Those of us who are familiar with Webwork, J2EE and many other Action Frameworks are quite comfortable with the use of request and response objects.

          Right now, I don't know how to pass data to an action or return value, such as a view, from an action so I can use/display it in a dockable window at the caller location.

          Comment


          • #6
            This is a good idea, IMHO. There are times when I would like to be able to execute a command and wait for a response. This is usually the case when the command pops up a modal dialog to query the user for something. There is currently no provision for return values in the command API. Commands can currently support parameters (ParameterizableActionCommandExecutor), but there is currently no support in the command framework to create buttons (via the command) that execute the command with parameters. This would solve my need of having multiple buttons on-screen that each execute the same command with different parameters (context).

            - Andy

            Comment


            • #7
              Andy,

              Could you post an example? I'm still trying to understand what your buttons would "pass" to these executing commands.

              One slight correction: a TargetableActionCommand gives you the ability to -- in a single all -- change the targeted "block of code" that executes for one or more bound actionable controls (like buttons or menu items.) You also get the abilty to retarget the "block of code" that determines when a given command (and thus all bound actionable controls) should be enabled.

              Re: action response: the command itself should encapsulate soliciting input, coordinating workflow to get a response (including displaying any model progress dialog), and choosing any new view for display. Guys, there is no "front controller" to take a generic response object / view descriptor and make a view selection like in a web environment: the command itself makes the view selection--for example displaying a success dialog.

              In general the controllers in a rich environment are more closely integrated with the views...-->the views (buttons) simply redirect user events to the controller for processing, and the controllers (commands) are responsible for coordinating the entire workflow ... including updating any other views...

              What would an ActionResponse object look like, who would process it, and what would they do with it? I don't see the benefit of such a construct here.

              Take this example: an "open customer command" that accepts a customer Id and displays a customer in the application editor area for editing. When executed in the "File" command group, this command pops up a input dialog to get the customer ID from the user. The user clicks OK, the command takes the input and retrieves the customer (perhaps dispatching in another thread, showing progress in a model dialog). Once retrieved, the command contacts the ApplicationPage and requests a new editor to open, passing in the customer domain object as input. This is what a controller should do is it not?

              Now, in Andy's case---it looks like he wants a "Open customer xyz" button that has a customer ID prebound in its context, passing that to the generic "open customer command". You could chain commands to do that:

              Code:
              ActionCommand command = new OpenSingleCustomerCommand(openCustomerCommand, "123456");
              JButton openCustomer123456 = command.createButton();
              Keith

              Comment


              • #8
                I seems possible to bind static command input parameters to bound actionable controls, since indeed the commands do manage the controls themselves.

                Still, I need a convincing case as obviously we have a million things to do these days ;-)

                Comment


                • #9
                  Further Suggestion

                  First, in a rich client environment the Application Context is the Front Controller. That, notwithstanding, there are many cases in an application where a command may be invoked from several points in an application context and each invoker may need to dynamically pass local data to the command and locally handle the result of the command execution differently. These are not insignificant requests.

                  Here is a quickie suggestion for a Request and a Response contruct:

                  Code:
                  public class ActionRequest implements java.io.Serializable 
                  {
                      private String toAction;      //Requested Action's Logical Name
                      private HashMap args;         //Requested Action's Constructor Args (String argName : Object argValue)
                      private Object sender;        //Action Request Source/Sender; Who Sent the Action Request?
                      private Object addressee;     //Action Result Destination; Who gets the result?
                          
                      //
                      // No-arg constructor
                      //
                      public ActionRequest() {}
                      //
                      // Place Getters and Setters Here
                      //
                      //
                      //Request Send Yourself With local Caller's data
                      //
                      public ActionResponse send(ActionResponse res)
                      {
                          try {
                  	    ActionCommand command = window.getCommandManager().getActionCommand(toAction);
                              return command.execute(this,res);//Presumes that execute() has been appropriately overloaded
                          }
                          catch (ClassNotFoundException cnfe){return null;}
                          catch (Exception e) {return null;}
                      }
                      //
                      //Request Send Yourself Without data
                      //
                      public void send()
                      {
                  	ActionCommand command = window.getCommandManager().getActionCommand(toAction);
                          command.execute();        
                      }
                      //
                      //Forward A New/Modified Request To Another Action
                      //
                      public ActionResponse forward(ActionRequest req,ActionResponse res)
                      {
                          try {
                              ActionCommand command = window.getCommandManager().getActionCommand(toAction);
                              return command.execute(this,res);//Presumes that execute() has been appropriately overloaded
                  	}
                          catch (ClassNotFoundException cnfe){return null;}
                          catch (Exception e) {return null;}
                      }
                  }
                  
                  //*************************************************************************************88
                  
                  public class ActionResponse implements java.io.Serializable 
                  {
                      private String code    = ActionIF.ERROR;  //Execution Return Code
                      private String message = "";              //Action-Side ActionResult Message
                      private Object result  = null;            //The ActionResult Object (Pre-Structured For Each Action)
                      private Object tAddress= null;            //Address Of The ActionResult's Destination Object
                      //
                      // No-arg constructor
                      //
                      public ActionResponse() {}
                      //
                      // Getters and Setters Go Here
                      //
                  }
                  Using command declarations in commands_context.xml, the above contructs can actually be simplified some.

                  A typical usage code, when sending data and processing return value, may look like this:

                  Code:
                  ActionResponse res = new ActionResponse();
                              ActionRequest req = new ActionRequest();
                              String returnCode = null;
                              String errorMsg = null;
                              HashMap args = new HashMap();
                              //
                              req.setToAction("actionCommandLogicalName");
                              //Set other request data;
                              args.put("//populate action's args as needed");
                  	    req.setArgs(args);
                  	    //
                              //Invoke The Action.
                              //
                              res = req.send(res);
                              //
                              if (res != null) {
                                  returnCode = (String)res.getCode();
                                  if (returnCode.trim().equals(ActionIF.SUCCESS)) {//Selected Action Succeeded
                                     //Extract return data and process as needed
                                  }
                                  else {
                  		   //Do some other processing
                  	        }
                              }
                  	    else {
                  		//Do Some other processing
                  	    }

                  When not sending local data or expecting return value, use exactly as is currently done in Spring RCP.

                  It is possible that Spring RCP's Action Framework can already provide the above capabilities. I just don't know how to use them, yet.

                  Comment


                  • #10
                    First, in a rich client environment the Application Context is the Front Controller
                    The ApplicationContext is a registry. It's not a front controller or request dispatcher.

                    It does act as a event bus for handling application events, decoupling event publishers from recepients.

                    I think you may have missed my point. My point is the Spring Rich GUI commands (controllers in the MVC triad) are closely integrated with their dispatching views. For example, let's take Petclinic. The "rename command" (controller) associated with a single "Owners View" executes when rename events are dispatched by the Owner JTree (view). Basically, a bound 'rename' button click event is routed to the centralized controller execution logic. There is one RenameCommand per OwnerView, and one OwnerView per ApplicationWindow. In general, command instances are scoped on a per window basis and managed by their containing views (they are not "true" singletons.)

                    On command execution, the controller encapsulates soliciting input from the user via a dialog (everything necessary to fulfill the rename action), performing the rename (delegating to the business layer on dialog finish, perhaps in another thread, perhaps to an XWork command), and updating any other views on success (displaying a sucess dialog). It also encapsulates behavior about when it should be enabled, and when it should be disabled (for example, enabled only when a single object is selected and the user has permission to rename.)

                    If "renaming" causes a object currently shared accross active views to change (for example, you have two windows open both showing an OwnerView), that's where ApplicationEvents come in. A single event --- published perhaps by the DAO or an aspect -- can be routed to multiple views displayed accross different windows, triggering those views to refresh (for example, on "object modified"). These views are attached as application listeners, wired into the context event bus. They know what events they should listen for.

                    Spring Rich MVC != Web MVC.

                    Comment


                    • #11
                      Continued

                      Keith,

                      I perfectly understand your point, and I do not dispute it.

                      I'm simply trying to alert you to a "not insignificant" need that some Spring RCP users have and one which may be easily accomodated by a slight modification to the Spring RCP Action Framework. Spring RCP will be the "richer", not the "poorer", with such a modification. It does not prevent any user from using the action framework the way you envision it

                      Comment


                      • #12
                        I just need concrete example scenarios demonstrating the need for such a feature. Spring Rich commands is not another XWork, nor should it be (or we'd just use XWork:-)). I could see a Spring Rich command delegating to an XWork command perhaps.

                        Comment

                        Working...
                        X