You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cocoon.apache.org by "Morrison, John" <Jo...@uk.experian.com> on 2002/08/23 09:41:50 UTC

FW: Separation of logic/content (Was: XSP Best Practise Question)

I think Alan has some good points.  Forwarded to cocoon-dev from -user.

J.

-----Original Message-----
From: Alan Hodgkinson [mailto:alan@softxs.ch]
Sent: Friday, 23 August 2002 9:45 am
To: cocoon-users@xml.apache.org
Subject: Separation of logic/content (Was: XSP Best Practise Question)



<warning>
  This is a long post and contains no Cocoon sample code.
</warning>


Abstract
--------

I present a way of naming links form actions that allow
you to separate links and forms from the next page to
display. I then raise questions about processing web
application events, specifically how to do this within
the existing Cocoon pipeline mechanism and maintain a
separation between processing of the input from one page
from the generation and display of the next page. I then 
say that Cocoon is great.


Introduction
------------

First Michael Edge wrote:

> I have a question regarding the use and purpose of XSP.  I 
> believe that XSP is an attempt to separate content/logic 
> from presentation

Then Leigh Dodds wrote:

> I've been thinking along the same lines recently, and am 
> still in two minds.

Join the club. Cocoon is just fine at separating presentation 
from content and logic. The problem is the separation of the 
content and logic.

I am relatively new to Cocoon and from what I've read of the 
documentation and in the mail archive, it seems that Cocoon 
is not yet ideal for web applications that have complex behavior 
based on user input (e.g. the page flow changes based on the 
data the user enters and the results of state changes in the 
session and database/business-object layer). It's possible to 
implement such applications with Cocoon, but it doesn't (yet) 
seem to be Cocoon's strength.

The yet to be implemented flowmap mentioned in the cocoon-dev
mailing list may be the solution we are looking for. (Seach 
for 'flowmap' in the cocoon-dev mailing list. The flowmap
is not yet fully defined, which means that now is the time
to provide your input).


It's About Control 
------------------

Thinking in Model-View-Controller (MVC) terms, the problem 
is how to separate the controller from the view/model. Note 
that we don't need to worry too much about mixing up the 
model and view, given Cocoon's flexible handling of XML 
data via pipelines.

Mapping MVC to web applications is, in my mind, a priori,
a mess, due to the fact that the requests (events) the
web application receives must ultimately be encoded as links 
and form actions in the HTML presented to the user. Thus 
some mixing of view and control is required. In a sense, 
we are trying to make the best of a bad situation.

Specifically, you have to embed links and form actions that 
define the input events to the controller directly in
the in web pages. There are ways arund this, but before I 
present a solution let's look at the types of input events 
we need to consider.


Links in Content
----------------

A typical web application the links can be divided into 
the following categories:

- Gotos: Links that simply take you to another part of 
  the application or web site. They typically make no 
  state change, except perhaps to note in a sessinon
  variable that you are now on a different page. These 
  are typically simple links.

- Content Display: Links that display statically or 
  dynamically generated data, but cause no state change. 
  This includes search operations, and links/buttons that
  display 'additional' information. Typically these are 
  links and buttons that include one or more key values
  E.g. an id value or form data that identifies the 
  record(s) to display or present in the next page or 
  form.

- State changes: Links that cause a state change. This
  includes business object and session state changes. 
  These can be quite complex operaions. Often these are 
  buttons associated with form data that the user enters.

How can you generate the embedded links and form actions 
in web content without creating explicit dependencies 
between the web pages (and also allow reuse of page 
components via Cocoon's aggregation)? The answer is to 
change the semantics of links and form actions. 


Goto Considered Harmful, Come-From is Fine
------------------------------------------

Instead of having the link tell the controller where it
wants to go, which is the controller's responsibility,
it should merely tell the controller which button/link
it 'is' or, in other words, where it is 'coming from'. 
For example, instead of a button having form action 
named:

  /cgi-bin/doUserUpdateSubmit.pl

which says what the web server should do, you would 
have:

  /userUpdateForm/changeName/submitButton

Which tells the controller exactly where the button was
pressed (and consequently what parameters it should
expect) and allows the controller the freedom do decide 
where to go next. More importantly, the developer is 
free to rearrange and reuse the pages and page components 
without changing the any of the links or form actions. 
All that is required is to maintain a mapping of the 
events and states to their action routines (this might 
be stored in something called, say, a flowmap :)

The whole point of this is to define a 'URI space' that 
enables you to specify, independent of physical location 
or file naming, all the application events that the 
controller must respond to. In other words, all the 
buttons and links that the web application contains.

Obviously you must also define the fields and parameters 
associated with the buttons and links, but at least you 
have turned all the links and form actions into events 
and dissociated them from the application flow.


So What About Cocoon?
---------------------

So far this all theory. The practical problem is:

  How do you do this in Cocoon?

<disclaimer>
  Being a Cocoon, newbie, I quickly reach my limits and 
  look to others to supply helpful suggestions. That said,
  here are some ideas. 
</disclaimer>

In the JSP/Servlet world, an obvious answer is the single
servlet solution mentioned by Liegh, where you have one 
servlet that implements all the control logic and dispatches 
to JSP pages for display. In servlet based applications 
you're free to write as many servlets as you want. In 
practice, pretty much everybody only writes one, which acts 
as a controller. 

There's probably going to be a parallel in Cocoon development
world. Perhaps we'll all end up writing a single master 
pipeline that calls 'internal-only' pipeline components
to do all the generation and display work.

How to implement the controller? Actions seem a reasonable 
choice. As explained in the Cocoon documentation, you can 
implement an action that sets sitemap parameter(s) that 
enables you to delegate the to the appropriate view based 
on the processing of the request parameters, session 
variables and business objects. This would require having
the 'master pipeline' described above.

The Controller could be implemented as a generic class
implementing Action, which could read an XML config file 
(called the flowmap or FSM-config). The config file would 
contain, in some form, the matrix of applictaion states and 
events and their mappings to the 'action pipelines'. 

It might be possible to encode the controller configuration 
directly in the pipeline portion of the sitemap, but I'm not 
sure that you want to include all that 'application code', 
(which is what this information really is, in the sitemap.


Pipelines Are So Cool That I Want Two of Them
---------------------------------------------

How can I separate the processing of page the form data 
from the page I just left, from the generation and display 
of the page I am going to present next?

Cocoon, with it's powerful pipelines, seduces you into
implementing your processing and display in a single pipeline 
(which might call sub-pipelines, but the point is that it's
a single event chain). The problem is, and you see it 
mentioned in the mailing lists, that once the pipeline is 
started, and the SAX events are flying towards the user,
there's no turning back or possibility for switching to 
another pipeline if you discover or decide you want to 
display something else.

When responding to a request I'd like to use one pipeline
composed of XSP logicsheets to perform the event processing 
and then be able to switch to another pipeline to generate 
the next page. All this should be managed by my controller 
which gets to pick the second pipeline based on the 
processing results from the first. 

Is this even possible in Cocoon? Are redirects an answer? 
My reading of the mail archives leads me to belive that this 
is probably not the correct solution. But perhaps there's
some other technique.

It is of course possible to implement all the page processing 
in Java, in an action class, which chooses the next page, 
but then I loose all the advantages of the logicsheets. 
Is there some trick we can use so that 'we have it all'?

I eagerly await your feedback and suggestions.

Best wishes to you all,

Alan Hodgkinson


P.S. Cocoon is great! 
---------------------

Why? Because it works and because we can determine its future 
with our code, commentary, rants, etc. Imagine trying to do this 
with a commercial product.

---------------------------------------------------------------------
Please check that your question  has not already been answered in the
FAQ before posting.     <http://xml.apache.org/cocoon/faq/index.html>

To unsubscribe, e-mail:     <co...@xml.apache.org>
For additional commands, e-mail:   <co...@xml.apache.org>


=======================================================================
Information in this email and any attachments are confidential, and may
not be copied or used by anyone other than the addressee, nor disclosed
to any third party without our permission.  There is no intention to
create any legally binding contract or other commitment through the use
of this email.

Experian Limited (registration number 653331).  
Registered office: Talbot House, Talbot Street, Nottingham NG1 5HF

---------------------------------------------------------------------
To unsubscribe, e-mail: cocoon-dev-unsubscribe@xml.apache.org
For additional commands, email: cocoon-dev-help@xml.apache.org


Re: FW: Separation of logic/content (Was: XSP Best Practise Question)

Posted by Christian Haul <ha...@dvs1.informatik.tu-darmstadt.de>.
On 23.Aug.2002 -- 08:41 AM, Morrison, John wrote:
> I think Alan has some good points.  Forwarded to cocoon-dev from -user.

You should definitely have a look at the flow as present in 2.1-dev
thanks to Ovidiu and Christopher. It has still some problems, but it
is much like having this single servlet.

I'm currently over a deadline so I'll just throw some code at you that
might illustrate use. When finished, cleaned up, and documented I plan
to put the whole thing up, either in applications or somewhere else on
the net.

*WARNING* The following is ugly code and if you might be offended by
such work, don't look at it. *WARNING*


The following is JavaScript code invoked through the sitemap using 

    <map:resource name="flow">
	  <!-- flow script -->
      <map:script src="flow.js"/>
    </map:resource>

	<map:resource name="function">
	  <!-- invoke a JavaScript flow function -->
	  <map:call function="{target}">
		<map:parameter name="prefix" value="{vquadrat-def:base-url}/protected/admin/internal"/>
	  </map:call>
	</map:resource>

in addition, the following is needed:

      <map:match pattern="kont/*">
		<!-- protected continuations -->
        <map:continue with="{1}"/>
	  </map:match>


Documentation is currently very sparse, but this is (pre-)alpha
anyway. There is a samll but complete example in the 2.1-dev snapshots.

function login(uriPrefix){
  var i;
  var j;
  var validation;
  var validation2;
  var msg;
  var tmp;
  var tmp2;
  var destination;
  var menuitem = "login";

  prefix=uriPrefix.slice(uriPrefix.indexOf('/',1));
  
  // save original destination from request-URI
  destination = inputValue("URI", "");
  log.debug("original destination is "+destination);
  log.debug("original destination == 'login' "+(destination=="login"));
  log.debug("original destination indexOf('protected/') "+destination.indexOf("protected/"));
  if (destination == "login" || destination.indexOf("protected/") == -1) {
    destination = "protected/welcome";
  } else if (destination.lastIndexOf("/logout")==destination.length-"/logout".length) {
    destination = "login";
  } 
  log.debug("destination is "+destination);
  

  i = 0;
  j = 0;
  msg = "";
  do {
    i++;
    validation = null;
    validation2 = null;
    tmp = inputValue("vquadrat-constants","base-url")+"/internal/login";
    tmp2 = inputValue("vquadrat-constants","base-url")+"/internal_new/login";

    do {
      // send login page
      if (j==0) {
        mySendPage(tmp2,{}, menuitem);
        j++;
      } else {
        mySendPage(tmp,{}, menuitem);
      }
      validation = act("vq-validator","",{"validate-set":"login"});
      log.debug("validation = "+(validation==null? "null" : validation));
    } while (validation == null);

    // check if account is blocked
    validation2 = act("vq-dbquery","",{"table-set":"is-not-blocked"});
    log.debug("account blocked? validation2 = "+(validation2==null? "null" : validation2));
    if (validation2 == null) {
      msg="The system could not log you in. Your account may have been blocked now for some minutes.";
      validation = null;
    } else {
      msg="";
      // check if password is ok
      validation = act("vq-dbsel","",{"table-set":"login"});
      log.debug("passwd ok? validation = "+(validation==null? "null" : validation));
      if (validation == null) {
      // login failed, block account
        msg="The system could not log you in. Your account may have been blocked now for some minutes.";
        validation2 = act("vq-dbquery","",{"table-set":"block-login"});
        log.debug("lock! validation2 = "+(validation2==null? "null" : validation2));
      } else {
        // login OK, set session attribute / clear failcount
        outputSet("vq-session","userdata", validation);
        outputSet("vq-session","logged-in", "true");
        outputCommit("vq-session");
        validation = act("vq-dbquery","",{"table-set":"block-clear"});
        log.debug("reset lock? validation = "+(validation==null? "null" : validation));
      }
    }
  } while((i<5)&&(validation==null));

  // send original destination
  mySendPage(destination,{}, menuitem);

  // done
}


	Chris.
-- 
C h r i s t i a n       H a u l
haul@informatik.tu-darmstadt.de
    fingerprint: 99B0 1D9D 7919 644A 4837  7D73 FEF9 6856 335A 9E08

---------------------------------------------------------------------
To unsubscribe, e-mail: cocoon-dev-unsubscribe@xml.apache.org
For additional commands, email: cocoon-dev-help@xml.apache.org