ChannelToBeanProxy

An idea for an IChannel that delegates all of its methods to a Spring-configured IChannel instance. This allows IChannels to receive dependencies (DataSources, IPersonAttributeDao instances, other service beans) via Dependency Injection.

Singletons and Prototypes

Spring allows you to specify not just singleton beans but also prototypes. On each request for a prototype bean the Spring BeanFactory will provide a new instance of that bean, configured as specified. This is important because the IChannel API is such that we have one IChannel instance per channel-in-layout per user session. No two users share an IChannel instance. No two instances of a multiply instantiated channel (say, the user has the RSS Reader channel twice) are the same IChannel instance.

Of course, if a particular IChannel relied upon no configuration, was, say, the boringest of idempotent read-only channels (think, the footer channel), then it wouldn't have to be a prototype in the Spring configuration, and everyone could use the same instance.

Code sketch

This is a code skectch for an IChannel that reads a StaticChannelData parameter to determine the name of the Spring-configured bean to which it should delegate.

ChannelToBeanProxy
package org.jasig.portal.channels;

import org.jasig.portal.ChannelRuntimeData;
import org.jasig.portal.ChannelRuntimeProperties;
import org.jasig.portal.ChannelStaticData;
import org.jasig.portal.IChannel;
import org.jasig.portal.PortalEvent;
import org.jasig.portal.PortalException;
import org.jasig.portal.spring.PortalApplicationContextFacade;
import org.xml.sax.ContentHandler;

/**
 * This IChannel delegates to a Spring-configured instance of 
 * IChannel. Expected usage is to configure a prototype IChannel instance via
 * Spring behind the PortalApplicationContextFacade with a particular name
 * and then configure this ChannelToBeanProxy instance with that name as
 * the value of the ChannelStaticData parameter 'targetBeanName'.
 * 
 * Note that the word "Proxy" is used in the name of this class in the same
 * spirit as the Acegi FilterToBeanProxy, the Struts ActionToBeanProxy, etc - 
 * we aren't talking about Java Proxies in the sense of the Reflection API.
 * 
 * @version $Revision:$ $Date:$
 */
public class ChannelToBeanProxy 
    implements IChannel {
    
    /**
     * The name of the ChannelStaticData parameter we will read to
     * get the name of the bean from the PortalApplicationContextFacade to
     * which we should delegate.
     */
    public static final String TARGET_BEAN_NAME_SD_PARAM = "targetBeanName";
    
    /**
     * Delegate IChannel which we are proxying.
     */
    private IChannel delegate;

    
    public void setStaticData(ChannelStaticData sd) throws PortalException {
        
        // lookup the name of the bean that defines our delegate
        String beanName = sd.getParameter(TARGET_BEAN_NAME_SD_PARAM);
        if (beanName == null) {
            throw new IllegalStateException("ChannelToBeanProxy requires the static data parameter [" 
                    + TARGET_BEAN_NAME_SD_PARAM 
                    + "]; this parameter was not set in the static data we received [" + sd + "]");
        }
        
        // obtain our delegate.
        this.delegate = (IChannel) PortalApplicationContextFacade.getPortalApplicationContext().getBean(beanName, IChannel.class);
        
        // proxy the setStaticData call.
        this.delegate.setStaticData(sd);
    }

    public void setRuntimeData(ChannelRuntimeData rd) throws PortalException {
        this.delegate.setRuntimeData(rd);
    }

    public void receiveEvent(PortalEvent ev) {
        this.delegate.receiveEvent(ev);
    }

    public ChannelRuntimeProperties getRuntimeProperties() {
        return this.delegate.getRuntimeProperties();
    }

    public void renderXML(ContentHandler out) throws PortalException {
        this.delegate.renderXML(out);
    }

}