Hello,

Any new ideas that could improve Orchestra would be great. I'm not sure at the moment exactly what this "rewrite" would do; what exactly are you proposing?

Well, basically I'd refactor the ConversationContext so that it's actually the main conversation of Orchestra. The conversation itself is almost independent of Spring (of course, there's still an according implementation of the Scope interface, but it will be implemented way easier). It's possible to nest conversations, i.e. a there's a certain hierarchy of conversations.

Each conversation has got it's own lifecycle and therefore it's possible to register so-called "conversation listeners" in order to hook logic into such lifecycle phases.

- listener.conversationStarted(ConversationEvent event);
This method will be called once a conversation starts as the name implies. The default implementation requires the user to start conversations explicitly, for example by calling "manager.startConversation()" where "manager" is a ConversationManager that you can inject into your beans (or lookup via an EL-expression).

- listener.conversationResumed(ConversationEvent event);
This method will be called once the user accesses a URL with the according request parameter of the conversation, i.e. if the user accesses the URL "http://localhost:8080/something.jsf?conversationId=1";.

- listener.conversationSuspended(ConversationEvent event);
This method will be called once such a request as above has been processed.

- listener.conversationEnded(ConversationEvent event);
This method will be called once the user invalidates a conversation.

The following example describes the most obvious usecase:
JpaPersistenceListener
- listener.conversationStarted():
 Create the persistence context and put it into the conversation.
- listener.conversationResumed():
 Bind the persistence context of the conversation
- listener.conversationSuspended():
 Unbind the persistence context of the conversation
- listener.conversationEnded():
 Close the persistence context

No additional proxy class is needed in that case and a persistence context is available to the whole application (you don't have to call DAO methods through conversation-scoped beans). If you don't persistence context support, then just don't configure the JpaPersistenceListener.

Those who know Spring Web Flow will think that I'm just proposing to do it like Spring Web Flow does - and yeah, they're right as I think that's the way to go. However, there's still the need for a persistence interceptor as you're still able to use the current conversation model of Orchestra. The difference is that such conversations will be nested within the main conversation of Orchestra and not within a ConversationContext.

In doing so, it's not required to specify the conversation you'd like to use on your bean definitions, as there's just one conversation: the current one and Orchestra is responsible for determining that one. Of course, this approach doesn't allow you to use two different conversations on the same view, but then you can still use the previous conversation model of Orchestra.

The SpringSingleConversationScope (or whatever it was called like) was a start in the right direction, but I think it's wrong to implement a single conversation-model on a named conversation-model. I'd propose to do it the other way around.

regards,
Bernhard Huemer

On 10/27/2008 +0100,
Simon Kitching <[EMAIL PROTECTED]> wrote:
Hi Bernhard,

It's great to see some design discussion about Orchestra!

Bernhard Huemer schrieb:
Hi folks,

I've been using Orchestra for a few months now and in doing so I've
been questioning some major implementation decisions, to be more
specific I really want to discuss if it's really necessary to bind
beans to a certain conversation (and therefore also a certein
persistence context!). Let me give you some examples:


Example 1)
I've developed some views for a search dialog that I wanted to use in
at least two different conversations. Everything worked fine for the
first conversation. The following code listing should give you an idea
of the problem.

///

view.xhtml
 <h:inputText value="#{conversationBean.value}" />

spring-bean-config.xml
 <bean id="conversationBean" class="..." scope="conversation.manual"
   orchestra:conversationName="conversationA" />

\\\

Maybe some of you have realized my problem by now: The bean somehow
depends on the conversation and as the view depends on the bean I
can't use it with a different conversation (or at least I'm missing a
major feature of Orchestra). :-f

The approach we (Mario & I) use in these kind of situations is to
deliberately *not* share a conversation between the calling and called
pages. We pass "input parameters" to the called page (your search
dialog) and it returns some values. But it doesn't use the conversation
of the caller. Of course this means that it cannot access the
persistence context of the caller; in particular it cannot perform
persistence operations within the persistence context associated with
the caller. In the case of a "search" type page, that means that the
search dialog just returns the *key* of the record that the user
selected and the caller must then load that object again by key. We
don't find that any major inconvenience.

I actually think this design is an advantage; when a java method calls a
method on a different class, the called code cannot mess with the
private members of the caller, and this is *good*. Equally, when a JSF
view "calls" another, I don't think that allowing the called view to
mess with the conversation-state of the caller is good. JSF already is
such a "loose" programming language that we are losing all the benefits
of a strictly-typed programming language; JSF apps can easily degenerate
into spaghetti code and I think allowing free access to
conversation-state from anywhere makes things more dangerous (though
possibly more convenient in the short term).

Mario's reply mentioned the new "orchestra flow" stuff that I have been
working on. However that works in the same way, just a little more
convenient: the called view(s) still run in their own environment
without access to the caller except for parameters that are explicitly
passed in. In fact, orchestra-flow isolates things even more strongly as
the called flow runs not just in a new conversation, but in a completely
different conversation-context so cannot access existing beans in other
conversation-contexts at all.

An alternative approach might be for the called view to require some
kind of "helper" object to be passed to it. You can do this via
f:setPropertyActionListener in the calling page. The helper object can
provide methods to do persistence operations, and they will run in
whatever conversation the helper object is in, regardless of what
conversation the "called" view's backing bean is in. Of course the
helper object could be the backing bean for the calling view itself.
Note that I haven't tried this; however I can't see any reason why it
wouldn't work.
Example 2)
In a different part of the same application I've created a view that
should serve for three different usecases. Basically the view doesn't
change for these usecases at all, but the logic of the backing bean
does. My first approach was to determine the specific page bean at
runtime in the action method that navigates to this view. This action
method was supposed to register the specific page bean under a common
name. So somehow it can be said that I wanted to use "polymorphic beans".

///

public String action() {
  Conversation conversation = getCurrentConversation();

  switch (criterium) {
    case CASE_A:
      conversation.setAttribute("commonBean", specificBeanA);
      break;

    case CASE_B:
      conversation.setAttribute("commonBean", specificBeanB);
      break;

    case CASE_C:
      conversation.setAttribute("commonBean", specificBeanC);
      break;

    default:
      throw new IllegalStateException(); // shouldn't occur anway
  }

  return outcome;
}

view.xhtml
 <h:commandButton action="#{commonBean.save}" />

\\\

However, that wouldn't work for two reasons:
 - Orchestra only knows how to resolve Spring beans as the bean
definition determines the conversation being used (well, Orchestra
doesn't know how to resolve anything, actually it's the
EL-/VariableResolver of Spring that does this job in this case). It's
only possible to resolve such variables if the view knows which
conversation it should access (i.e. if one would develop a custom
ELResolver that knows how to resolve expressions like
#{conversation.conversationA.commonBean} - *cough* problem of the
first example *cough*).
 - Orchestra wouldn't create a persistence context for that bean as
the persistence interceptor only gets attached to Spring beans. (No,
I'm not telling you to modify the setAttribute method so that advices
will be attached. I'm rather telling you to use conversation listeners
instead for persistence support, just like Web Flow does).

The "bean name" is just a label that then somehow retrieves an
appropriate bean. But a single label can only convey one piece of
information. You want to specify two pieces of information:
 (a) which bean to use
 (b) which conversation to associate it with
I just cannot see how one name can imply two things.

Could you please explain what you mean by "conversation listeners"? I
don't know what you mean here, but am very interested..

I was able to work around this issue by introducing an additional
indirection, i.e. I'm resolving that bean with the expression
#{pageAccessor['commonBean']}, but that's just not intuitional.
Agreed. But this is then using *two* labels to specify *two* values: the
first label (pageAccessor) provides information about which conversation
and the second label (commonBean) specifies which bean. You clearly want
to flatten this down to a single label that expresses both pieces of
information but I can't currently see how you propose to do that...


However, please don't get me wrong. I don't want to hear anything
about certain workarounds that I could have used in these cases as I
think that these usecases aren't exceptional enough to justify
workarounds. Of course, the current approach has got its advantages
too (e.g. switching arbitrarily between different conversations as
there is no hierarchical structure), but at least I haven't needed
this feature yet.

Summarizing it can be stated that I'd propose you to rewrite
conversation handling for the next major release and I'd be willing to
contribute. Note that I don't want to drop support for these "named
conversations", but I think that this usecase is not the default one
for conversations.
Any new ideas that could improve Orchestra would be great. I'm not sure
at the moment exactly what this "rewrite" would do; what exactly are you
proposing?

Regards,
Simon



Reply via email to