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