| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> |
| <html> |
| <head> |
| <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
| <title>Portal: Event Handling</title> |
| <link href="http://purl.org/DC/elements/1.0/" rel="schema.DC"> |
| <meta content="Overview" name="DC.Subject"> |
| <meta content="Carsten Ziegeler" name="DC.Creator"> |
| </head> |
| <body> |
| |
| <h1>Overview</h1> |
| |
| <p> |
| This document gives an overview of the event handling of the portal engine. |
| </p> |
| |
| <p> |
| The sample portal that comes with the Cocoon distribution contains several |
| working samples for event handling. |
| </p> |
| |
| |
| <h1>Introduction</h1> |
| |
| <p> |
| The event handling is a central mechanism used in the portal engine. Every |
| change (changes in status or layout, links etc) is propagated through an |
| event. The portal uses the publisher/subscribe paradigm, so each component |
| that is interested in a specific event can subscribe itself for this type |
| of event. In addition each component can send out events. |
| </p> |
| |
| <p> |
| The processing of a portal request (a request send to the Cocoon portal) |
| is divided into two phases: event handling and rendering. In the first phase |
| all events are processed. For example if the user clicks a link this |
| triggers an event that is published. Any receiver of this event might |
| in turn fire new events that are published as well. |
| </p> |
| |
| <p> |
| When all events are processed, the first phase is finished and the second |
| phase, the rendering, is started. At this point of time all event handling |
| and all information exchange should be finished. |
| </p> |
| |
| |
| <h1>Events and the request/response cycle</h1> |
| |
| <p> |
| In the Portal, an event is represented by a Java object. This event object |
| contains all necessary information to process the event. So, in most cases an |
| event contains the object to modify, what to modify and the value to set. |
| For example, the minimize event for minimizing a coplet, contains the |
| coplet, the information to change the window state and the value "minimize" |
| to set. |
| </p> |
| |
| <p> |
| There are different types of events: a type for changing the window state, |
| a type for removing a coplet, a type for links that are clicked by the user etc. |
| Each event type is represented by a Java class (or interface). |
| </p> |
| |
| <p> |
| A component that processes for example the window state request is subscribed |
| to this minimize event (or: the corresponding class/interface) |
| and when such an event is fired, it changes the window state of the |
| coplet to minimize. Every data this component needs is stored in |
| the event. This is a very important detail: the event is not directly processed |
| (in this case) by the object that is changed (the coplet) but by a central |
| subscribed component that changes the coplet. This is because of the |
| publisher/subscribe mechanism used: many components in the portal can subscribe |
| to the same event type if they are interested. So, each component that is |
| interested in an event needs all information about this event. That's why |
| all data is stored in the event itself. |
| </p> |
| |
| <p> |
| Let's have a look how such an event is created: |
| </p> |
| |
| <pre class="code"> |
| Event event; |
| event = new ChangeCopletInstanceAspectDataEvent( |
| copletInstanceData, |
| "size", |
| SizingStatus.STATUS_MINIMIZEDD); |
| </pre> |
| |
| <p> |
| |
| <em>Event</em> is just a marker interface, the concrete implementation |
| <em>ChangeCopletInstanceAspectDataEvent</em> implements this interface |
| and requires three pieces of information: the CopletInstanceData, |
| the information about what to change (size) and the new value. |
| </p> |
| |
| <p> |
| All events must implement the marker interface <em>Event</em>, so if |
| a component is interested in all Events it could subscribe itself |
| using this event type. |
| </p> |
| |
| <p> |
| If you want to fire an event, you have to publish it. Therefore you |
| need the event manager, a central portal component. You can lookup this |
| component, fire the event and release the manager again. If you fire |
| the event, the event is directly published to all subscribed components. |
| </p> |
| |
| <pre class="code"> |
| EventManager manager = null; |
| try { |
| manager = serviceManager.lookup(EventManager.ROLE); |
| manager.getPublisher().publish(event); |
| } finally { |
| serviceManager.release(manager); |
| } |
| </pre> |
| |
| <p> |
| As noted above, the event will be directly fired. But usually in a portal |
| application, events are not fired directly but are invoked by some user |
| action. This means, the user clicks on a link in the browser, the |
| request is targetted at Cocoon and the portal invokes (fires) the correct events. |
| </p> |
| |
| <p> |
| For this, a link (or a form action) must know, which event it should |
| fire, if it is clicked. So, in other words, a link is associated with |
| a concrete event. But on the other site, an event is a Java object and we can only use |
| strings in URLs. So how does this work? |
| </p> |
| |
| <p> |
| The part of the portal that generates the link, creates the event object |
| with all necessary data, transforms this event into a usable URI and this |
| URI is the target of the link. When the user clicks on this link, the portal |
| transforms the URI back into the Java event object and fires the event. |
| </p> |
| |
| <p> |
| The transformation Event->URI->Event is done by another portal component, |
| the link service. Most portal components (apart from the event manager) |
| are available through another central component, the portal service. So |
| you need to have access to the portal service component. (Renderers e.g. |
| don't have to lookup the service by itself, they get it as a method |
| parameter). |
| </p> |
| |
| <pre class="code"> |
| PortalService service = null; |
| try { |
| service = serviceManager.lookup(PortalService.ROLE); |
| LinkService ls = service.getComponentManager().getLinkService(); |
| |
| String uri = getLinkURI( event ); |
| |
| // create a link that references the uri |
| } finally { |
| serviceManager.release(service); |
| } |
| </pre> |
| |
| <p> |
| That's all you have to do: create the event object, get the link service, |
| transform the event into a URI using the service and then create the |
| (html) link using the URI. Everything else is handled by the portal for you. |
| </p> |
| |
| <p> |
| In addition, you can transform several events into one single link. So |
| you can create links, the user can click, that do several things at |
| the same time (minimizing one coplet and maximizing another one etc.). |
| The link service offers corresponding methods for this. |
| </p> |
| |
| |
| <h1>Changing the State of a Coplet</h1> |
| |
| <p> |
| In most cases you want to change the state of a coplet because the user |
| performed an action. The portal engine provides you with some events |
| that you can directly use. |
| </p> |
| |
| <ul> |
| |
| <li>CopletJXPathEvent</li> |
| |
| <li>ChangeCopletInstanceAspectDataEvent</li> |
| |
| </ul> |
| |
| <p> |
| The <em>CopletJXPathEvent</em> requires again three pieces of information: |
| the coplet instance data to change, the JXPath expression that defines the |
| data to change and the value: |
| </p> |
| |
| <pre class="code"> |
| Event event = new CopletJXPathEvent(copletInstanceData, |
| "attributes/username", |
| username); |
| </pre> |
| |
| <p> |
| In the previous chapter, we already saw an example of the usage of the |
| <em>ChangeCopletInstanceAspectDataEvent</em>. |
| </p> |
| |
| <p> |
| It is of course possible that you write your own events for |
| changing the state of a coplet. But in this case make sure that |
| your own event implements the interface <em>CopletInstanceEvent</em>. |
| This helps the portal engine in tracking if a coplet has been changed. |
| </p> |
| |
| |
| <h1>Subscribing to Events</h1> |
| |
| <p> |
| If you are interested in events, you can subscribe to a specific event |
| type. As events are Java objects, you subscribe for all events of a |
| specific interface or class (and all of the subclasses). |
| Subscribing is done using the event manager: |
| </p> |
| |
| <pre class="code"> |
| EventManager manager = null; |
| try { |
| manager = serviceManager.lookup(EventManager.ROLE); |
| manager.getRegister().subscribe( myComponent ); |
| } finally { |
| serviceManager.release(manager); |
| } |
| </pre> |
| |
| <p> |
| The component you subscribe must implement the Subscriber interface: |
| </p> |
| |
| <pre class="code"> |
| Subscriber interface: |
| |
| public Class getEventType(); |
| |
| public void inform( Event event ); |
| </pre> |
| |
| <p> |
| The getEventType() method returns the class/interfaces of the events |
| the component is interested and each time such an event occurs, |
| the inform() method is invoked. |
| </p> |
| |
| <p> |
| For example one central component in the portal subscribes for |
| all events dealing with coplets, so it returns <em>CopletInstanceEvent</em> |
| as the class (interface) in getEventType(). |
| </p> |
| |
| |
| <h1>Inter Coplet Communication</h1> |
| |
| <p> |
| A very interesting feature of the portal is inter-coplet communication. |
| The demo portal already has a simple sample where the name of an |
| image selected in an image gallery is transfered to a different coplet. |
| </p> |
| |
| <p> |
| Now, there is only one (minor) problem: in the cocoon portal coplets |
| (or more precisly CopletInstanceData objects) are not components but |
| just data objects. So, a coplet can't directly register itself as |
| a subcriber for events. |
| </p> |
| |
| <p> |
| Remember that we mentioned earlier on a central component that processes |
| the change events for coplets? So, this is basically one possibility: if |
| you want to pass information from one coplet to another one, create |
| a CopletJXPathEvent and pass the information to the other coplet. |
| </p> |
| |
| <p> |
| Imagine a form coplet where the user can enter a city. When this form is |
| processed by the form coplet, it can generate one (or more) CopletJXPathEvents |
| and push the entered city information to a weather coplet and a hotel guide |
| coplet. So, these two coplets display the information about the selected |
| city. |
| </p> |
| |
| |
| <h1>The Coplet Transformer</h1> |
| |
| <p> |
| Apart from the possibility to create events from within your Java code, |
| it's also possible to create events from within a pipeline by using |
| for example the coplet transformer. It listens for elements with the |
| namespace "http://apache.org/cocoon/portal/coplet/1.0". |
| </p> |
| |
| <h2>The coplet element</h2> |
| <p> |
| The coplet element has nothing to do with events :) It can be used |
| to include information about the current coplet in the SAX stream: |
| </p> |
| <pre class="code"> |
| ... |
| <coplet:coplet select="attributes/name"/> |
| ... |
| </pre> |
| <p> |
| The coplet element can only be used inside a coplet pipeline, but |
| not in the main portal pipeline. The select attribute defines an |
| JXPath expression that is used to fetch the value that is included |
| in the stream. |
| </p> |
| |
| <h2>The link element</h2> |
| <p> |
| The link element creates a link that will trigger an event if the |
| user clicks this link: |
| </p> |
| <pre class="code"> |
| ... |
| <coplet:link coplet="COPLET_ID" path="JXPATH" value="TO_SET"/> |
| <coplet:link layout="LAYOUT_ID" path="JXPATH" value="TO_SET"/> |
| ... |
| </pre> |
| <p> |
| This element generates an HTML link which will eiter trigger an |
| event to change a coplet instance data or a layout based on |
| the JXPath and the value provided. |
| </p> |
| |
| |
| <h1>Configuring Subscribers</h1> |
| |
| <p> |
| In the previous chapters we saw one possibility to subscribe: dynamically |
| in some Java code. This requires that - of course - this code is executed |
| at some point of time. This is a solution for dynamic subscribers, which |
| means a subscriber that is only "available" if a specific feature of |
| the portal is used. If the feature is available, the "feature" subscribes |
| itself (or another component). |
| </p> |
| |
| <p> |
| However, this adds an exta burdon to the development of own events and |
| their subscribers. Therefore it is possible to configure subscribers |
| in the cocoon.xconf. These subscribers are instantiated by the |
| portal engine on startup of Cocoon and subscribed by the portal |
| engine. |
| </p> |
| |
| <p> |
| You have two possibilites, you can either subscribe Avalon components or |
| classes. In the first case, you configure the role of the component. |
| Then the portal engine looks up this component and subscribes it. |
| </p> |
| |
| <p> |
| If you configure a class, the portal engine creates an instance of this |
| class using the no-argument constructor and subscribes this instance. |
| For convenience, this instance can implement the Avalon lifecycle |
| interface like LogEnabled or Serviceable. |
| </p> |
| |
| <p> |
| The configuration takes place in the cocoon.xconf as a configuration for |
| the event manager: |
| </p> |
| |
| <pre class="code"> |
| ... |
| <component class="org.apache.cocoon.portal.event.impl.DefaultEventManager" |
| logger="portal" |
| role="org.apache.cocoon.portal.event.EventManager"> |
| ... |
| <!-- add a new instance of each class as a subscriber: --> |
| <subscriber-classes> |
| <class name="org.apache.cocoon.portal.event.subscriber.impl.DefaultJXPathEventSubscriber"/> |
| </subscriber-classes> |
| <!-- add each component as a subscriber (the component should be thread safe): --> |
| <subscriber-roles> |
| <role name="org.apache.cocoon.portal.samples.location.LocationEventSubscriber"/> |
| </subscriber-roles> |
| </component> |
| ... |
| </pre> |
| |
| <p> |
| In the sample configuration above, one class is subscribed (the |
| <em>DefaultJXPathEventSubscriber</em>) and one Avalon component |
| (the <em>LocationEventSubscriber</em>). |
| </p> |
| |
| <p> |
| So, if you write your own events and your own subscribers you can either dynamically |
| add them during execution or statically add them by configuration as shown above. |
| </p> |
| |
| |
| <h1>Further Information</h1> |
| |
| <p> |
| The event.impl package contains all currently processed events, so you can |
| study the events and see how to create them. In general most events are |
| created inside the renderers, especially the renderer aspects that render |
| specific details (e.g. the sizing buttons for a coplet). So, you can have |
| a look at the code as well. |
| </p> |
| |
| <p> |
| There are several transformers that help in creating events inside a Cocoon |
| pipeline. For example the <em>coplet transformer</em> can be used to |
| create links that contain events to change the status of a coplet or a layout |
| object. The gallery sample uses this transformer as a demo. |
| </p> |
| |
| |
| </body> |
| </html> |