Ok, so I've got a prototype that works, but has issues. I'm at a point where
insight from some people in the know would probably help a lot. I would also
like to describe what I've done, to see if you guys like the direction.

As far as I can see, you can't use a simple collection of listeners approach
to the portlet event listeners because it doesn't work across portlets.
I have implemented a prototype which uses a behaviour which checks if
current request is an event request, and if so provides the Event object to
the behaviour
The problem with this implementation is that the onPortletEvent really needs
to be called separately from the components render cycle, otherwise a
component may already get rendered that would otherwise have been affected
by the portlet event. That, or the behaviour effects are only visible to
it's containers children.

After some hacking around and some serious learning, I went with using a
IBheaviour to receive events from the portal and an object attached to
WebApplication to send events. One of the main reasons I went with
IBehaviour was so that the event handling would be called at the right
stage, when the page is loaded etc, because I wasn't sure where else to
start handling events. It basically works, but has issues with wicket state
for the receiving portlet, and doesn't start working until the receiving
portal has moved from '/', i.e. after the first redirect issues from Wicket.

it's a bit of a hack, where if it's an event request, the wicketURL is set
called. The event requests seem to resolve to RedirectPageRequestTargets,
and i'm not sure why those don't consider it required to call
response.sendRedirect, even though RENDER_TO_REDIRECT is on, but doing so
causes an infinite loop. I seems it has something to do with
RedirectPageRequestTargets representing a redirect already in progress- but
an event request isn't a redirect, at least not in the sense we want it to
be in. (technically, the first actionRequest which caused the event has
already called response.sendRedirect. But we still need the eventRequest to
call the same thing so that we can grab the correct wicket URL.) It appears
that requests which don't include an interface name, default to a
IRedirectListener. This makes sense i suppose because the user is being
redirect from an 'empty path' to an actual interface perhaps. Can we perhaps
force RedirectRequestTargetListener to use sendRedirect? Perhaps only when
it portlet mode?
The problem is, because the sendRedirect is never called, the resultant url
is not recorded and subsequent VIEW renders don't have the url and so render
back to root (/)
The problem with _this_ is that the portlet which is the subject of the
eventRequest, renders one state behind. I've modified
RedirectPageRequestTarget#processEvents to call
AbstractListenerInterfaceRequestTarget#onProcessEvents like
ListenerInterfaceRequestTarget does.

With RENDER_TO_REDIRECT on, why does this:
http://localhost:8080/non-gizmo-wicket-portlet-events/eventa/?wicket:interface=:11:basic-link:6:ILinkListener::
redirect to 
http://localhost:8080/non-gizmo-wicket-portlet-events/eventa/?wicket:interface=:11:6:::
while 
http://localhost:8080/non-gizmo-wicket-portlet-events/eventa/
doesn't redirect at all, and the browser points to:?
http://localhost:8080/non-gizmo-wicket-portlet-events/eventa/
Shouldn't you get:?
http://localhost:8080/non-gizmo-wicket-portlet-events/eventa/?wicket:interface=:0::::

This is the behaviour that 'appears' to be causing some problems with the
way I've implemented the events.

to work around this, I've added in a hack which sets the wicketUrl after the
event request is processed by the target portal:

WicketPortlet#processRequest:
.....

                if (actionRequest || eventRequest)
                {
                        // create the request dispatcher, to delegate the 
request to the wicket
filter
                        PortletRequestDispatcher rd =
getPortletContext().getRequestDispatcher(wicketURL);

                        if (rd != null)
                        {
                                // delegate to wicket filter - this is where 
the magic happens
                                rd.include(request, response);
                                if (wicketURL.equals(wicketFilterPath))
                                {
                                        wicketURL = wicketURL + 
"?wicket:interface=:0:0:::";
                                }
                                responseState.setRedirectLocation(wicketURL);

this allows it to get kicked off, but it's buggy, and inconsistent, but sort
of works.

After one sequence of events, I also start getting
ERROR - WebResponse                - Unable to redirect to:
/eventa/?wicket:interface=:0:1:::, HTTP Response has already been committed.
and the target portlet (eventa) stops re-rendering.

the other problem with this implementation is that the behaviour's aren't
triggered until the render request, where really it should be triggered in
the action request. The implication of this contridicts the portal spec
because if any event triggers another event, the behaviour of the portal
isn't defined, as events should only be triggered in action or event
requests.

The way it's used is like this:

                add(new ActiveMovieEventListener()
                {
                        public void onEvent(ActiveMoviePortletEvent event)
                        {
                                if (event.getMovie() != null)
                                        activeMovie = event.getMovie();
                        }
                });

and the important part of the event listener behaviour is here:
        /**
         * Unwraps the request objects, looking for the [EMAIL PROTECTED]
javax.portlet.Event}, passing the
         * [EMAIL PROTECTED] javax.portlet.Event#getValue()} onto the
         * [EMAIL PROTECTED] IPortletEventListener#onEvent(PortletEvent)} 
method. Quietly
no-ops if not currently
         * servicing an [EMAIL PROTECTED] EventRequest}.
         * 
         * @see
org.apache.wicket.behavior.AbstractBehavior#beforeRender(org.apache.wicket.Component)
         */
        @Override
        public void beforeRender(Component component)
        {
                super.beforeRender(component);
                Request request = component.getRequest();

                // unwrap our present, see if we got what we wanted for x-mas
                if (request instanceof ServletWebRequest)
                {
                        ServletWebRequest swr = (ServletWebRequest)request;
                        HttpServletRequest httpServletRequest = 
swr.getHttpServletRequest();
                        if (httpServletRequest instanceof 
PortletServletRequestWrapper)
                        {
                                PortletServletRequestWrapper psrw =
(PortletServletRequestWrapper)httpServletRequest;
                                ServletRequest request2 = psrw.getRequest();
                                if (request2 instanceof EventRequest)
                                {
                                        EventRequest er = 
(EventRequest)request2;
                                        Event event = er.getEvent();
                                        String eventName = event.getName();
                                        QName eventQName = event.getQName();
                                        Object eventValue = event.getValue();
                                        if (eventName.equals(this.eventName) ||
eventName.equals(eventQName.toString()))
                                        {
                                                onEvent((T)eventValue);
                                        }
                                }
                        }
                }

        }

And to send an event:

        ActiveMoviePortletEvent<MovieListPage> e = new
ActiveMoviePortletEvent<MovieListPage>(this, activeMovie);
        WebApplication.get().sendPortletEvent(e);

which goes to this magic:

        private void broadcastToPortletListeners(PortletEvent event)
        {
                Response response = RequestCycle.get().getResponse();
                Request request = RequestCycle.get().getRequest();

                if (request instanceof WebRequest)
                {
                        WebRequest wr = (WebRequest)request;
                        HttpServletRequest sr = wr.getHttpServletRequest();
                        // is there a better way to retrieve the ActionResponse 
other than this?
                        ActionResponse a =
(ActionResponse)sr.getAttribute(ActionResponse.class.getName());
                        if (a != null)
                        {
                                String canonicalName = 
event.getClass().getSimpleName();
                                a.setEvent(canonicalName, event);
                        }
                }
        }

as you can see, the ActionResponse is retrieved as a request attribute,
which was initially set in WicketPortlet#processAction(EventRequest,
EventResponse):

                // store the ActionResponse
                request.setAttribute(ActionResponse.class.getName(), response);

Those are the important bits. The patch is not ready to be submitted yet,
but if this doesn't lead to enough to talk about, then I can prepare it
properly and upload it.

I could also, in the patch I submit, port my example into wicket/examples
and include it in the patch.

Apart from API abstraction, another good thing to come of this is that the
framework manages the delegation of the event handling to the listening
component - without the user having to pass it themselves.

The other issue with this, which I don't think can be avoided with out some
massive wicket core changes, is that each eventRequest that's fired, causes
another complete requestCycle. I.e. REDIRECT_TO_RENDER always causes 2, well
with events we have 2+number of events recieved.

Some other questions / issues:

If the key WICKET_URL_PORTLET_PARAMETER is always the same, why bother
storing it as a request attribute? why not just reference the static
constant directly from classes that need it, rahter than looking it up? what
am i missing here? - after a little use, I see it sometimes changes to
_wuview.

In WicketPorlet, shouldnt the PARAM_VIEW_PAGE paramaters be called
PARAM_VIEW_MODE instead, as they represent the porlets' mode? and hence
forth refered to as portletMode not pageType?

Why does WicketPortlet#processActionResponseState not have a while loop
around it, while the resource request does? can it not also have redirects
from redirects?

How do we address the issue of users being able to access the backing filter
directly, bypassing the portal? I'm not sure how this would work because
Wicket needs to be running as a portlet in order to service the requests. It
doesn't even seem to be possible with the current implementation to provide
a configuration switch to 'not allow direct access'.

I've prototyped an event system using the suggest pattern. Because of the
way portals work, we still need string identifiers to id events, as all
interesting events have to be registered in the portlets deployment
descriptor. One idea could be to have some standard events with wicket
namespace names which are some typical events that someone would be
interested in - but i'm not sure what those events would be. 

Perhaps we could have a standard events that will get broadcast to any
listeners. After learning, it seems there would be quite a lot of overhead
in implementing something like this, because any standard events which are
broadcast, get sent as event requests to target portlets. however these
could be filtered with settings in your portlet.xml (just don't list the
standard event names as being processable or publishable(which would stop
them at the source)).

Potential show stopper - you can't trigger events from ajax requests,
because the ajax requests are implemented as resource requests, and
ResourceResponse doesn't implement StateAwareResponse (events are sent using
StateAwareResponse#setEvent). Stink. To change this, you would have to
change the way that ajax requests are handled, into actionRequests instead
of resourceRequests - I don't know if that's possible, or makes sense. It
may be a limitation we have to live with for now?


why is local variable is never read set to ignore in the project settings?
in fact why are most the unnecessary code section set to ignore?

Cheers guys, looking forward to your comments.


Antony Stubbs wrote:
> 
> Morning guys,
> 
> This is msg is of course regarding the continuation and the finishing of
> the
> Portal 2.0 wicket support. I plan on continuing from Thijs' work done on
> WICKET-1620 here:
> 
> Here are the two historical conversations regarding the topic
> -Portal 2 implementation:
> http://www.nabble.com/Portlet-2.0-implementation-in-Wicket-td17187909.html
> -original portal 1 implementation:
> http://www.nabble.com/A-new-proposal-for-Wicket-Portlet-support-td10863022.html
> 
> I met with Ate the other day in Utrecht, Netherlands and we discused
> various
> things, some of which was the strategy we would adopt going forward with
> the
> P2 development.
> 
> First thing is that we must come to a consensus with the Wicket team about
> the direction we will head.
> There are, as we see it, two options:
> 
> 1) leave 1.0 support behind on 1.3 branch and move forward with 2.0
> support
> on 1.4 branch
> 
> 2) try to support both at once in 1.4 - isolate portal 2.0 support to it's
> own package
> 
> My vote, and I think Ate agrees is to go with option 1 - leave 1.0 support
> behind.
> 
> We also need to have release goals for all this. I.e. release target for
> portlet javadoc (1.4?) target for 2.0 implementation (1.4? 1.4.1? or 1.5)
> 
> The most bbvious things left to be done are:
> 0. Documentation - there is lots of javadoc to be written about the
> current
> Portal support
> 1. Portal 2.0 event abstraction
> 2. Public render parameter abstraction
> 3. testing of the implementation on a couple of containers (e.g. Pluto,
> Jetspeed and Liferay)
> 4. writing some demo apps that use 2.0 features
> 5. potentially writing a module to support a portlet preferences container
> in web app mode, in order to have a complete wicket abstraction for user
> prefs. This would of course use the Portlet API support when running in a
> portlet context.
> 
> Regarding 2 - the event abstraction, we saw there being 3 different stages
> of this, the first stage being the target for 1.4, as lvl 2+ require large
> wicket refactoring.
> - level 1 - event listener registration, similar to Swing - lots of manual
> interpretation of events etc
> - level 2 - built in to Wicket event system with pre defined events on
> components - with portal specific events. So Wicket would have standard
> portlet types of events which it interprets and calls predefined
> interfaces
> in your code for. What those standard events would be, requires some more
> conceptualising.
> - level 3 - wicket specific events - extend the concept of events into
> wicket as a whole. e.g. inter component / panel event system. These events
> of a general broadcast type nature, could in a portlet environment, be
> made
> to cross the portlet boundaries in a fashion transparent to the user.
> 
> Regarding 2, there's not much work here to be done, except to decide on a
> nice way of abstracting the portal 2 api calls
> 
> if anyone is aware of any other issues that need to be chased up regarding
> 2.0 support, please raise them now or attach them to WICKET-1620.
> 
> Other issues Ate pointed out were:
> Dealing with portlet requests through forwarded requests as opposed to
> includes
> Dealing with streaming servers which don't support the option
> javax.portlet.renderHeaders option - using caching solution from Apache
> Bridges. (but there was some other gotcha wasn't there Ate?)
> Need to maintain the current support for containers which have no header
> contribution support by using inline javascript
> 
> 
> Regarding collaboration on this, well if I wasn't fully convinced with Git
> before, I certainly am now!
> 
> I have setup a fork of Wicket 1.4 trunk on GitHub, which I can track with
> Git, where I have created three branches. This was done off of the new
> Apache git mirrors available here:
> http://jukka.zitting.name/git/
> 
> 1. a javadoc improvement branch for things Portletish (strictly method and
> class level javadoc only, maybe some fields)
> 2. a portlet 2.0 development branch (this already had Thijs patch applied,
> and a couple of comments, refactors from me)
> 3. a merge branch of the two
> 
> Unless we decide that the javadoc can wait for the 2.0 implementation -
> which would be easier ;)
> 
> I plan on keeping the javadoc branch separate so that I can easily create
> a
> patch/push against the 1.4 trunk, as Ate and I thought it would be good to
> get the Javadoc we write into the code base as soon as possible, while the
> actual Portal 2.0 implementation can continue on.
> 
> The public github project is here:
> http://github.com/astubbs/wicket.get-portals2/tree/portal-javadoc
> 
> Install Git (if you're on Windows, I recommend Cygwin/Git) and then you
> can
> clone the repository using:
> git clone git://github.com/astubbs/wicket.get-portals2.git
> nb, you won't be able to push to it though unless you make a github
> account
> 
> and you can switch to the javadoc branch for example using:
> git checkout origin/portal-javadoc
> 
> Here is some good Git information:
> http://github.com/guides/home
> 
> Cheers,
> looking forward to hearing your thoughts.
> 
> -- 
> ___________________________
> http://stubbisms.wordpress.com/
> 
> 
> -----
> ___________________________ 
> http://stubbisms.wordpress.com 
> 


-----
___________________________

http://stubbisms.wordpress.com http://stubbisms.wordpress.com 
-- 
View this message in context: 
http://www.nabble.com/Finishing-Portal-2.0-development-tp19909563p20227405.html
Sent from the Wicket - Dev mailing list archive at Nabble.com.

Reply via email to