Vincent Massol wrote:
Hi Sergiu,
On Mar 15, 2008, at 8:11 AM, Sergiu Dumitriu wrote:
Hi,
How do you feel about the next usage scenarios for the observation
component?
A. Simple observing of changes
[snip]
B. Simple observing of actions
[snip]
C. Precondition/Postcondition enforcing
The previous two use cases allow a simple ping that something has
already happened. (Actually, the listeners can also change the passed
document, as the actual object is sent.) But I'd like to be able to
write a listener that can say "this is not allowed, please stop this
action", or "these changes are not OK, don't save this document".
So, a
precondition can be that "/save/" cannot be performed between 6PM and
7AM, which would be a needed feature in some companies, or that "/
view/"
cannot be performed on the "SectionA" space from the IP addresses like
192.168.2.*, which belongs to the SectionB users (these are action
events). And a postcondition can be that the XWiki.XWikiPreferences
can
only be updated by changing XWikiGlobalRights objects, other types of
changes can only be performed by a superadmin only, or that adding
some
content that looks like spam is not allowed (these are document
events).
So, on top of the previous use cases, I'd like to add a "phase" to the
events, which indicates if the event is sent before or after the
change
(or we can just add new events), and a new type of exception thrown by
the listeners, which is not ignored. Right now, if a listener throws
an
exception, it is ignored. This exception, when received by the code
that
sends the notification, should stop the normal processing and display
"action not allowed" + ex.getMessage to the user.
My first reaction is that I'm not really fan of this approach. The way
I had planned to implement this in the V2.0 architecture is by either:
1) replacing existing component with a different implementation. This
is when the current behavior need to be modified/replaced by another
one.
2) depending on the component, introduce new component. For example
the Action Manager in the new action component allows defining new
Actions that can be inserted before or after existing actions (or even
replace existing actions but that's 1)).
To summarize, I would agree with your idea except that with components
there's another way of doing it and I don't think we should have too
many ways. If you think about how you'd implement your idea you'll end
up creating a state machine component to which actions are hooked to.
This is what the Action Manager component is meant to do, although for
the moment I have resisted introducing a FSM since I wanted to see the
exact need first and with 1) and 2) I thought we could already do
quite a lot (not sure what we cannot do to be honest).
This allows introducing/changing ONE new functionality, by completely
replacing a whole component, but it isn't flexible enough IMHO. Using
events also allows:
- chaining several completely different "behaviors"
- writing one listener for all the actions, instead of replacing several
Actions just to introduce a check
- defining a behavior as a Groovy script placed inside a document, so
adding a new -- let's say -- security feature will not require writing a
component, packaged as a jar, but just installing a wiki application,
controllable from the wiki, like the IRC bot.
I don't see a need for a FSM, as the place where these events should be
sent are already clear, and the old notification already sends pre- and
post-notifications, just that all the errors are ignored.
D. Rendering
hooks
While working on the SkinExtensions and trying to write a better #toc
macro, I was faced with the problem that there's no way a plugin can
change the rendered content at the end. Yes, it can alter it line by
line, and in theory there is an endRenderingHook sent with the
content,
but that is actually buggy, as an empty string is sent instead of the
content, and the result is discarded. Still, even if fixed, this is
sent
only for *rendered* content, not *parsed* content. My initial idea was
to change the PluginInterface by adding two new methods that will be
called before and after the content was generated, but that is a bad
idea, since the PluginInterface is already too big and has too many
types of methods. And since the new rendering component is not yet
available, I thought that we can also use events for this.
So, instead of writing new methods in the PluginInterface, we can add
other type of events, which gets sent in the parsing/rendering
process.
That way, we don't need to have a plugin that implements all these
methods, and adding a new feature will require just sending a new type
of event, instead of changing an interface (which is dangerous).
The source would be an object holding the string, which can be
modified
by the listeners. Other information could be the content document, if
there is one, the rendering/parsing context, anything else?
Again my first reaction is that I'm no fan... :) The reason is that I
have this implemented in the new rendering component.
First any piece of code can get the parsed content of a text having
any kind of syntax (Element Blocks or DOM). Second, it's possible to
write any Macro that will manipulate this DOM and make any
modification to it. This is how the Toc Macro is going to be rewritten
actually.
However you're right in that right now all I have planned for are
Macros and these are triggered by the macro syntax in the text to
parse/rendering. So it's a good idea to add a small FSM in the
rendering component since we already need an order in the macro
execution but also to add the ability to register macros to be
executed regardless of whether they appear in the parsed text or not.
At first I thought that the new rendering part would be enough, but then
I realized that in order for some code to be executed, it must exist in
the source, as a macro or something.
Then I thought that we can make several tree walkers that are executed,
instead of one that transforms the tree into XHTML, or PDF, or RSS, or a
different wiki markup, or whatever, but I'm not sure this is a good
idea, either.
I don't like the events approach for this (especially the fact that the
event is used for passing data around), but for the moment it seemed a
simpler approach while waiting for the rendering engine.
E. Other
generic events
Like FlushCacheEvent, StartupEvent, ShutdownEvent, VirtualInitEvent,
VirtualShutdownEvent (sent when a wiki is deleted by the
WikiManagerEvent), Application[Added/Enabled/Disabled/Removed]Event
(sent by the ApplicationManager), Space[Added/Removed]Event (sent by
the
SpaceManagerPlugin), etc.
+1 to those.
Thanks
-Vincent
_______________________________________________
devs mailing list
devs(a)xwiki.org
http://lists.xwiki.org/mailman/listinfo/devs
--
Sergiu Dumitriu
http://purl.org/net/sergiu/