Hi guys,
============ Background ============
My recent commit r559394[1] broke Cocoon again. More specifically, you will get
java.lang.NoClassDefFoundError: org/apache/commons/beanutils/ConvertUtils
or JXPath's error that it can't find org.apache.cocoon.forms.generation.JXMacrosHelper.createHelper function. This error has been
mentioned[2] by Reinhard long time ago and Daniel gave response[3]:
You are only supposed to have direct access to java.*, IIUC, packages
from FOM for other packages you must have a "Package." prefix, i.e.
value="#{Package.org.apache.cocoon.forms.generation.JXMacrosHelper.createHelper($cocoon/consumer,$cocoon/request)}
This is the behaviour that is implemented in
o.a.c.environment.TemplateObjectModelHelper.addJavaPackages.
So I thought that I messed some initialization and wasted handful amount of time to track down the problem. I was wrong. JXPath uses it's
default set of functions if none is registered (and it *is* the case, so Daniel's response was completely misleading) that gives access to
all Java classes. The error I'm getting is because of createHelper signature:
public static JXMacrosHelper createHelper(XMLConsumer consumer, Request
request, String locale)
It expects o.a.c.e.Request but after applying[1] object model contains
HttpServletRequest thus JXPath's evaluation fails.
============ Possible solutions ============
The half of the work is already done - we know why it is broken, now I would like to discuss with you possible fixes. We have three distinct
options:
1. Store o.a.c.e.Request implementation in Object Model and provide wrappers that would be manually created in places where needed.
HttpRequest (which is only implementation of Request interface that we can consider) wraps HttpServletRequest. It means we could have
several wrapping levels. *Messy* to my taste.
2. Make a o.a.c.e.Request interface extend HttpServletRequest interface. That would be a solution, because we could store such
implementation in Object Model as HttpServletRequest and cast where its needed to o.a.c.e.Request (JXPath would do it automatically for us).
Such solution was mentioned several times before, like here[4] by Carsten. I haven't evaluated this option much. Does it still makes sense,
what about concerns raised by Carsten? Are there any other?
3. Forget about back compatibility and clean up the mess, start switching to HttpServletRequest *right away*. In most cases the changes
would be trivial (changing imports in various classes). Of course I don't want to get rid of Cocoon's Request interface everywhere at once
because it's not in scope of my GSoC work. I only want to get rid of it where it's needed.
Ok, you guessed it I prefer third option. I've performed already incompatible changes like Ajax and Forms migration to servlet-service-fw[5]
and provided quite good migration guides like[6]. People were happy with it... I think that the key was they clearly knew what to do with
their applications in order to get back them to usable state. Isn't it a case, Felix?
I could provide such guide for incompatible changes if they occurred. After playing with Cocoon's internals for some time I think that there
is no way that I can improve something significantly and stay 100% back-compliant because current functionality relies on hacks, plumbing,
bungs and other weird things. Undoubtedly, we are approaching to my sermonizing.
============ Sermonizing ============
I must confess that I'm fed up with Cocoon's internals.
Cocoon's internal are fucking crazy.
Sorry for strong words but I must give vent to my emotions. Now more
matter-of-fact arguments:
* We have two different request objects original servlet's one and our own one. The problems that this situation causes has been
discussed above.
* We have at least four different object models!
a) objectModel map that is passed to components
b) FlowscriptObjectModel
c) TemplateObjectModel (if template was called by flowscript)
d) TemplateObjectModel (if template wasn't called by flowscript)
Items c) and d) do not differ only in data user puts while calling sendPage, it would be too obvious. It differs in some internal,
obscure areas so some functionality working in template called from flowscript (like constructing new objects) will not work if you just
call it out of flow execution scope.
* Have you been aware of "this" variable in object model? (argh need to be specific, in object model available in template). Take a look
at this:
<jx:set var="cformsDummy"
value="${cformsHelper.generateRepeaterWidgetLabel(widget, id, this['widget-id'])}"/> (from
jx-macros.xml)
I guess that it's the only place that is used, it was implemented in bug-possibly way (don't want to bore you with details) and was
not covered by tests nor documented ANYWHERE.
This lists could be much longer. This lists make me sick because it was never as much painful to develop something Cocoon-related as this
GSoC work. I was prepared to some obstacles, world is not perfect, I knew that our internals are far from perfection. Now I know that they
are fucking crazy, period.
WDYT?
[1] http://article.gmane.org/gmane.text.xml.cocoon.cvs/24848
[2] http://article.gmane.org/gmane.text.xml.cocoon.devel/50595
[3] http://article.gmane.org/gmane.text.xml.cocoon.devel/50621
[4] http://article.gmane.org/gmane.text.xml.cocoon.devel/59143
[5] https://issues.apache.org/jira/browse/COCOON-1991
[6] http://cocoon.zones.apache.org/daisy/cdocs-forms/g1/1351.html
--
Grzegorz Kossakowski
http://reflectingonthevicissitudes.wordpress.com/