| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cocoon.portal.event.impl; |
| |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.avalon.framework.activity.Disposable; |
| import org.apache.avalon.framework.activity.Initializable; |
| import org.apache.avalon.framework.configuration.Configurable; |
| import org.apache.avalon.framework.configuration.Configuration; |
| import org.apache.avalon.framework.configuration.ConfigurationException; |
| import org.apache.avalon.framework.container.ContainerUtil; |
| import org.apache.avalon.framework.context.Context; |
| import org.apache.avalon.framework.context.ContextException; |
| import org.apache.avalon.framework.context.Contextualizable; |
| import org.apache.avalon.framework.logger.AbstractLogEnabled; |
| import org.apache.avalon.framework.service.ServiceException; |
| import org.apache.avalon.framework.service.ServiceManager; |
| import org.apache.avalon.framework.service.ServiceSelector; |
| import org.apache.avalon.framework.service.Serviceable; |
| import org.apache.avalon.framework.thread.ThreadSafe; |
| import org.apache.cocoon.ProcessingException; |
| import org.apache.cocoon.components.ContextHelper; |
| import org.apache.cocoon.portal.PortalService; |
| import org.apache.cocoon.portal.event.Event; |
| import org.apache.cocoon.portal.event.EventConverter; |
| import org.apache.cocoon.portal.event.EventManager; |
| import org.apache.cocoon.portal.event.Publisher; |
| import org.apache.cocoon.portal.event.Receiver; |
| import org.apache.cocoon.portal.event.Register; |
| import org.apache.cocoon.portal.event.Subscriber; |
| import org.apache.cocoon.portal.event.aspect.EventAspect; |
| import org.apache.cocoon.util.ClassUtils; |
| import org.apache.cocoon.util.Deprecation; |
| |
| /** |
| * This is the default implementation of the event manager. |
| * |
| * @author <a href="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a> |
| * @author <a href="mailto:volker.schmitt@basf-it-services.com">Volker Schmitt</a> |
| * |
| * @version CVS $Id$ |
| */ |
| public class DefaultEventManager |
| extends AbstractLogEnabled |
| implements EventManager, |
| Serviceable, |
| Initializable, |
| ThreadSafe, |
| Configurable, |
| Disposable, |
| Contextualizable, |
| Publisher, Register { |
| |
| private final String rootEventType = Event.class.getName(); |
| private Class eventClass; |
| /** The list of all subscribers. */ |
| private List subscribers = new ArrayList(); |
| /** The list of all receivers */ |
| private Map receivers = new HashMap(); |
| |
| private ServiceManager manager; |
| private Configuration configuration; |
| |
| protected EventAspectChain chain; |
| |
| protected ServiceSelector aspectSelector; |
| |
| protected Context context; |
| |
| /** The portal service */ |
| protected PortalService service; |
| |
| /** Introspected receiver classes */ |
| protected Map receiverClasses = new HashMap(); |
| |
| /** |
| * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager) |
| */ |
| public void service(ServiceManager manager) throws ServiceException { |
| this.manager = manager; |
| this.service = (PortalService)manager.lookup(PortalService.ROLE); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.portal.event.EventManager#getPublisher() |
| */ |
| public Publisher getPublisher() { |
| return this; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.portal.event.EventManager#getRegister() |
| */ |
| public Register getRegister() { |
| return this; |
| } |
| |
| /** |
| * Helper method to get the current object model |
| */ |
| protected Map getObjectModel() { |
| return ContextHelper.getObjectModel( this.context ); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.avalon.framework.configuration.Configurable#configure(org.apache.avalon.framework.configuration.Configuration) |
| */ |
| public void configure(Configuration conf) |
| throws ConfigurationException { |
| this.configuration = conf; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.avalon.framework.activity.Disposable#dispose() |
| */ |
| public void dispose() { |
| if (this.manager != null) { |
| if ( this.chain != null) { |
| this.chain.dispose( this.aspectSelector ); |
| } |
| this.manager.release( this.aspectSelector ); |
| this.aspectSelector = null; |
| this.manager.release(this.service); |
| this.service = null; |
| this.manager = null; |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.avalon.framework.activity.Initializable#initialize() |
| */ |
| public void initialize() |
| throws Exception { |
| this.eventClass = Class.forName( this.rootEventType ); |
| if ( this.getLogger().isDebugEnabled() ) { |
| this.getLogger().debug("Initialising eventClass " + this.eventClass); |
| } |
| |
| // FIXME - the following configuration is not portal specific, it's global! |
| // subscribe all configured roles |
| Configuration roles = this.configuration.getChild("subscriber-roles", false); |
| if ( roles != null ) { |
| Configuration[] rolesConf = roles.getChildren("role"); |
| for(int i=0; i<rolesConf.length;i++) { |
| final Configuration current = rolesConf[i]; |
| final String name = current.getAttribute("name"); |
| |
| Subscriber subscriber = null; |
| try { |
| subscriber = (Subscriber) this.manager.lookup(name); |
| Deprecation.logger.warn("Subscriber is deprecated. Please convert the following component to a Receiver: " + subscriber.getClass().getName()); |
| this.subscribe(subscriber); |
| } finally { |
| this.manager.release(subscriber); |
| } |
| } |
| } |
| // subscribe all configured classes |
| Configuration classes = this.configuration.getChild("subscriber-classes", false); |
| if ( classes != null ) { |
| Configuration[] classesConf = classes.getChildren("class"); |
| for(int i=0; i<classesConf.length;i++) { |
| final Configuration current = classesConf[i]; |
| final String name = current.getAttribute("name"); |
| |
| Deprecation.logger.warn("Subscriber is deprecated. Please convert the following component to a Receiver: " + name); |
| Subscriber subscriber = (Subscriber) ClassUtils.newInstance(name); |
| ContainerUtil.enableLogging(subscriber, this.getLogger()); |
| ContainerUtil.contextualize(subscriber, this.context); |
| ContainerUtil.service(subscriber, this.manager ); |
| ContainerUtil.initialize(subscriber); |
| this.subscribe(subscriber); |
| } |
| } |
| // subscribe all configured receiver roles |
| roles = this.configuration.getChild("receiver-roles", false); |
| if ( roles != null ) { |
| Configuration[] rolesConf = roles.getChildren("role"); |
| for(int i=0; i<rolesConf.length;i++) { |
| final Configuration current = rolesConf[i]; |
| final String name = current.getAttribute("name"); |
| |
| Receiver receiver = null; |
| try { |
| receiver = (Receiver) this.manager.lookup(name); |
| this.subscribe(receiver); |
| } finally { |
| this.manager.release(receiver); |
| } |
| } |
| } |
| // subscribe all configured receiver classes |
| classes = this.configuration.getChild("receiver-classes", false); |
| if ( classes != null ) { |
| Configuration[] classesConf = classes.getChildren("class"); |
| for(int i=0; i<classesConf.length;i++) { |
| final Configuration current = classesConf[i]; |
| final String name = current.getAttribute("name"); |
| |
| Receiver receiver = (Receiver)ClassUtils.newInstance(name); |
| ContainerUtil.enableLogging(receiver, this.getLogger()); |
| ContainerUtil.contextualize(receiver, this.context); |
| ContainerUtil.service(receiver, this.manager ); |
| ContainerUtil.initialize(receiver); |
| this.subscribe(receiver); |
| } |
| } |
| |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.portal.event.Publisher#publish(org.apache.cocoon.portal.event.Event) |
| */ |
| public void publish( final Event event ) { |
| this.send(event); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.portal.event.Register#subscribe(org.apache.cocoon.portal.event.Subscriber) |
| */ |
| public void subscribe( final Subscriber subscriber ) { |
| if ( !this.eventClass.isAssignableFrom( subscriber.getEventType() ) ) { |
| throw new RuntimeException("Invalid event type " + subscriber.getEventType() |
| +" for subscriber " + subscriber); |
| } |
| |
| if ( this.getLogger().isDebugEnabled() ) { |
| this.getLogger().debug( "Subscribing event " + subscriber.getEventType().getName() ); |
| } |
| |
| // Add to list but prevent duplicate subscriptions |
| if ( !this.subscribers.contains( subscriber ) ) { |
| this.subscribers.add( subscriber ); |
| if ( this.getLogger().isDebugEnabled() ) { |
| this.getLogger().debug( "Subscribed Event " + subscriber.getEventType().getName() ); |
| this.getLogger().debug( "Subscribers now active: " + this.subscribers.size() ); |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.portal.event.Register#unsubscribe(org.apache.cocoon.portal.event.Subscriber) |
| */ |
| public void unsubscribe( Subscriber subscriber ) { |
| |
| if ( !this.eventClass.isAssignableFrom( subscriber.getEventType() ) ) { |
| throw new RuntimeException("Invalid event type " + subscriber.getEventType() |
| +" for unsubscribing " + subscriber); |
| } |
| if ( this.subscribers.contains( subscriber ) ) { |
| this.subscribers.remove( subscriber ); |
| if ( this.getLogger().isDebugEnabled() ) { |
| this.getLogger().debug( "Unsubscribed Event " + subscriber.getEventType().getName() ); |
| this.getLogger().debug( "Subscribers now active: " + this.subscribers.size() ); |
| } |
| } else { |
| this.getLogger().warn( "Subscriber " + subscriber + " not found" ); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.cocoon.portal.event.EventManager#processEvents() |
| */ |
| public void processEvents() |
| throws ProcessingException { |
| if ( this.configuration != null ) { |
| synchronized ( this ) { |
| if ( this.configuration != null ) { |
| try { |
| this.aspectSelector = (ServiceSelector) this.manager.lookup( EventAspect.ROLE+"Selector"); |
| this.chain = new EventAspectChain(); |
| this.chain.configure(this.aspectSelector, this.configuration.getChild("event-aspects")); |
| } catch (ConfigurationException ce) { |
| throw new ProcessingException("Unable configure component.", ce); |
| } catch (ServiceException ce) { |
| throw new ProcessingException("Unable to lookup component.", ce); |
| } |
| this.configuration = null; |
| } |
| } |
| } |
| DefaultEventAspectContext context = new DefaultEventAspectContext(this.chain); |
| EventConverter converter = null; |
| PortalService service = null; |
| try { |
| service = (PortalService)this.manager.lookup(PortalService.ROLE); |
| converter = (EventConverter) this.manager.lookup(EventConverter.ROLE); |
| Publisher publisher = this.getPublisher(); |
| |
| converter.start(); |
| |
| // Invoke aspects |
| context.setEventPublisher( publisher ); |
| context.setObjectModel(this.getObjectModel()); |
| context.setEventConverter(converter); |
| context.invokeNext( service ); |
| |
| converter.finish(); |
| |
| } catch (ServiceException ce) { |
| throw new ProcessingException("Unable to lookup component.", ce); |
| } finally { |
| this.manager.release(converter); |
| this.manager.release(service); |
| } |
| |
| } |
| |
| /** |
| * @see org.apache.avalon.framework.context.Contextualizable#contextualize(org.apache.avalon.framework.context.Context) |
| */ |
| public void contextualize(Context context) |
| throws ContextException { |
| this.context = context; |
| } |
| |
| /** |
| * @see org.apache.cocoon.portal.event.EventManager#send(org.apache.cocoon.portal.event.Event) |
| */ |
| public void send(Event event) { |
| if ( this.getLogger().isDebugEnabled() ) { |
| this.getLogger().debug("Publishing event " + event.getClass()); |
| } |
| for ( Iterator e = this.subscribers.iterator(); e.hasNext(); ){ |
| Subscriber subscriber = (Subscriber)e.next(); |
| if (subscriber.getEventType().isAssignableFrom(event.getClass()) |
| && (subscriber.getFilter() == null || subscriber.getFilter().filter(event))) { |
| if ( this.getLogger().isDebugEnabled() ) { |
| this.getLogger().info("Informing subscriber "+subscriber+" of event "+event.getClass()); |
| } |
| subscriber.inform(event); |
| } |
| } |
| for (Iterator re = this.receivers.entrySet().iterator(); re.hasNext(); ) { |
| final Map.Entry current = (Map.Entry)re.next(); |
| final Receiver receiver = (Receiver)current.getKey(); |
| final List methodInfos = (List)current.getValue(); |
| boolean found = false; |
| final Iterator ci = methodInfos.iterator(); |
| while ( !found && ci.hasNext() ) { |
| final MethodInfo info = (MethodInfo)ci.next(); |
| if ( info.eventClass.isAssignableFrom(event.getClass()) ) { |
| if ( this.getLogger().isDebugEnabled() ) { |
| this.getLogger().info("Informing receiver "+receiver+" of event "+event.getClass()); |
| } |
| try { |
| info.method.invoke(receiver, new Object[] {event, this.service}); |
| } catch (Exception ignore) { |
| this.getLogger().warn("Exception during event dispatching on receiver " + receiver |
| +" and event " + event, ignore); |
| } |
| found = true; |
| } |
| } |
| } |
| } |
| |
| protected static final class MethodInfo { |
| |
| public Class eventClass; |
| public Method method; |
| } |
| |
| protected synchronized List introspect(Class receiverClass) { |
| List result = (List)this.receiverClasses.get(receiverClass.getName()); |
| if ( result == null ) { |
| result = new ArrayList(); |
| Method[] methods = receiverClass.getMethods(); |
| for(int i=0; i<methods.length; i++ ) { |
| final Method current = methods[i]; |
| if ( current.getName().equals("inform") ) { |
| final Class[] params = current.getParameterTypes(); |
| if ( params.length == 2 |
| && params[1].getName().equals(PortalService.class.getName())) { |
| if ( this.eventClass.isAssignableFrom( params[0] ) ) { |
| MethodInfo info = new MethodInfo(); |
| info.eventClass = params[0]; |
| info.method = current; |
| result.add(info); |
| } |
| } |
| } |
| } |
| if ( result.size() == 0 ) { |
| result = null; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * @see org.apache.cocoon.portal.event.EventManager#subscribe(org.apache.cocoon.portal.event.Receiver) |
| */ |
| public void subscribe(Receiver receiver) { |
| List infos = this.introspect(receiver.getClass()); |
| if ( infos == null ) { |
| throw new RuntimeException("Invalid event receiver type: " + receiver); |
| } |
| |
| // Add to list but prevent duplicate subscriptions |
| List eventClassesForReceiver = (List)this.receivers.get(receiver); |
| if ( eventClassesForReceiver == null ) { |
| this.receivers.put(receiver, infos); |
| } |
| if ( this.getLogger().isDebugEnabled() ) { |
| for(int i=0; i<infos.size();i++) { |
| this.getLogger().debug( "Receiver " + receiver + " subscribed for event: " + ((MethodInfo)infos.get(i)).eventClass.getName() ); |
| } |
| } |
| } |
| |
| /** |
| * @see org.apache.cocoon.portal.event.EventManager#unsubscribe(org.apache.cocoon.portal.event.Receiver) |
| */ |
| public void unsubscribe(Receiver receiver) { |
| if ( this.getLogger().isDebugEnabled() ) { |
| this.getLogger().debug( "Receiver " + receiver + " unsubscribed."); |
| } |
| this.receivers.remove(receiver); |
| } |
| |
| } |