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.
