Package org.jasig.portal

Source Code of org.jasig.portal.ChannelRenderer$IWorker

/**
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig 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.jasig.portal;

import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.apache.commons.collections.map.ReferenceMap;
import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasig.portal.channels.portlet.IPortletAdaptor;
import org.jasig.portal.channels.support.IChannelTitle;
import org.jasig.portal.channels.support.IDynamicChannelTitleRenderer;
import org.jasig.portal.portlet.url.RequestType;
import org.jasig.portal.properties.PropertiesManager;
import org.jasig.portal.security.IPerson;
import org.jasig.portal.spring.locator.CacheFactoryLocator;
import org.jasig.portal.spring.locator.JpaInterceptorLocator;
import org.jasig.portal.utils.SAX2BufferImpl;
import org.jasig.portal.utils.SetCheckInSemaphore;
import org.jasig.portal.utils.cache.CacheFactory;
import org.jasig.portal.utils.threading.BaseTask;
import org.jasig.portal.utils.threading.Task;
import org.jasig.portal.utils.threading.TrackingThreadLocal;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.orm.jpa.JpaInterceptor;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;

/**
* This class takes care of initiating channel rendering thread,
* monitoring it for timeouts, retreiving cache, and returning
* rendering results and status.
* @author Peter Kharchenko  {@link <a href="mailto:pkharchenko@interactivebusiness.com"">pkharchenko@interactivebusiness.com"</a>}
* @version $Revision: 20117 $
* @deprecated IChannel rendering code will be replaced with portlet specific rendering code in a future release
*/
@Deprecated
public class ChannelRenderer
    implements IChannelRenderer, IDynamicChannelTitleRenderer
{

    protected final Log log = LogFactory.getLog(getClass());

    /**
     * Default value for CACHE_CHANNELS.
     * This value will be used when the corresponding property cannot be loaded.
     */
    private static final boolean DEFAULT_CACHE_CHANNELS = false;

    public static final boolean CACHE_CHANNELS=PropertiesManager.getPropertyAsBoolean("org.jasig.portal.ChannelRenderer.cache_channels", DEFAULT_CACHE_CHANNELS);

    public static final String[] renderingStatus={"successful","failed","timed out"};
   
    public static final String SYSTEM_WIDE_CHANNEL_CACHE = "org.jasig.portal.ChannelRenderer.SYSTEM_WIDE_CHANNEL_CACHE";

    protected final IChannel channel;
    protected final ChannelRuntimeData rd;
    protected Map<String, ChannelCacheEntry> channelCache;
    protected Map<IChannel, Map<String, ChannelCacheEntry>> cacheTables;

    protected boolean rendering;
    protected boolean donerendering;

    protected Thread workerThread;

    protected IWorker worker;
    protected Future<?> workTracker;

    protected long startTime;
    protected long timeOut = java.lang.Long.MAX_VALUE;

    protected boolean ccacheable;

    protected static ExecutorService tp=null;

    protected SetCheckInSemaphore groupSemaphore;
    protected Object groupRenderingKey;

    /**
     * Default contstructor
     *
     * @param chan an <code>IChannel</code> value
     * @param runtimeData a <code>ChannelRuntimeData</code> value
     * @param threadPool a <code>ThreadPool</code> value
     */
    public ChannelRenderer (IChannel chan,ChannelRuntimeData runtimeData, ExecutorService threadPool) {
        Validate.notNull(chan, "IChannel can not be null");
        this.channel=chan;
        this.rd=runtimeData;
        this.rendering = false;
        this.ccacheable=false;
        tp = threadPool;

        this.groupSemaphore=null;
        this.groupRenderingKey=null;
    }


    /**
     * Default contstructor
     *
     * @param chan an <code>IChannel</code> value
     * @param runtimeData a <code>ChannelRuntimeData</code> value
     * @param threadPool a <code>ThreadPool</code> value
     * @param groupSemaphore a <code>SetCheckInSemaphore</code> for the current rendering group
     * @param groupRenderingKey an <code>Object</code> to be used for check ins with the group semaphore
     */
    public ChannelRenderer (IChannel chan,ChannelRuntimeData runtimeData, ExecutorService threadPool, SetCheckInSemaphore groupSemaphore, Object groupRenderingKey) {
        this(chan,runtimeData,threadPool);
        this.groupSemaphore=groupSemaphore;
        this.groupRenderingKey=groupRenderingKey;
    }

    /**
     * Obtains a content cache specific for this channel instance.
     *
     * @return a key->rendering map for this channel
     */
    // XXX is this thread safe?
    Map<String, ChannelCacheEntry> getChannelCache() {
        if (this.channelCache == null) {
            if ((this.channelCache = this.cacheTables.get(this.channel)) == null) {
                this.channelCache = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT, 2, .75f, true);
                this.cacheTables.put(this.channel, this.channelCache);
            }
        }
        return this.channelCache;
    }


    /**
     * Set the timeout value
     * @param value timeout in milliseconds
     */
    public void setTimeout (long value) {
        this.timeOut = value;
    }

    public void setCacheTables(Map<IChannel, Map<String, ChannelCacheEntry>> cacheTables) {
        this.cacheTables=cacheTables;
    }

    /**
     * Informs IChannelRenderer that a character caching scheme
     * will be used for the current rendering.
     * @param setting a <code>boolean</code> value
     */
    public void setCharacterCacheable(boolean setting) {
        this.ccacheable=setting;
    }

  /**
   * Start rendering of the channel in a new thread.
   * Note that rendered information will be accumulated in a
   * buffer until outputRendering() function is called.
   * startRendering() is a non-blocking function.
   */
  public void startRendering ()
  {
    // start the rendering thread

    final IWorker targetWorker = new Worker(this.channel, this.rd);
   
    // Obtain JcrInterceptor bean
    final JpaInterceptor jpaInterceptor = JpaInterceptorLocator.getJpaInterceptor();

    // Proxy worker so that Jpa EntityManager will be properly handled
    final ProxyFactoryBean pfb = new ProxyFactoryBean();
    pfb.setTarget(targetWorker);
    pfb.setInterfaces(targetWorker.getClass().getInterfaces());
    pfb.addAdvice(jpaInterceptor);
  
    this.worker = (IWorker)pfb.getObject();

    this.workTracker = tp.submit(this.worker); // XXX is execute okay?
    this.rendering = true;
    this.startTime = System.currentTimeMillis ();
  }

    public void startRendering(SetCheckInSemaphore groupSemaphore, Object groupRenderingKey) {
        this.groupSemaphore=groupSemaphore;
        this.groupRenderingKey=groupRenderingKey;
        this.startRendering();
    }

    /**
     * <p>Cancels the rendering job.
     **/
    public void cancelRendering()
    {
        if (null != this.workTracker) {
            this.workTracker.cancel(true);
        }
    }

  /**
   * Output channel rendering through a given ContentHandler.
   * Note: call of outputRendering() without prior call to startRendering() is equivalent to
   * sequential calling of startRendering() and then outputRendering().
   * outputRendering() is a blocking function. It will return only when the channel completes rendering
   * or fails to render by exceeding allowed rendering time.
   * @param out Document Handler that will receive information rendered by the channel.
   * @return error code. 0 - successful rendering; 1 - rendering failed; 2 - rendering timedOut;
   */
    public int outputRendering (ContentHandler out) throws Throwable {
        int renderingStatus=completeRendering();
        if(renderingStatus==RENDERING_SUCCESSFUL) {
            SAX2BufferImpl buffer;
            if ((buffer=this.worker.getBuffer())!=null) {
                // unplug the buffer :)
                try {
                    buffer.setAllHandlers(out);
                    buffer.outputBuffer();
                    return RENDERING_SUCCESSFUL;
                } catch (SAXException e) {
                    // worst case scenario: partial content output :(
                    log.error( "outputRendering() : following SAX exception occured : "+e);
                    throw e;
                }
            } else {
                log.error( "outputRendering() : output buffer is null even though rendering was a success?! trying to rendering for ccaching ?");
                throw new PortalException("unable to obtain rendering buffer");
            }
        }
        return renderingStatus;
    }


    /**
     * Requests renderer to complete rendering and return status.
     * This does exactly the same things as outputRendering except for the
     * actual stream output.
     *
     * @return an <code>int</code> return status value
     */

    public int completeRendering() throws Throwable {
        if (!this.rendering) {
            this.startRendering ();
        }
        boolean abandoned=false;
        long timeOutTarget = this.startTime + this.timeOut;

        // separate waits caused by rendering group
        if(this.groupSemaphore!=null) {
            while(!this.worker.isSetRuntimeDataComplete() && System.currentTimeMillis() < timeOutTarget && !this.workTracker.isDone()) {
                long wait=timeOutTarget-System.currentTimeMillis();
                if(wait<=0) { wait=1; }
                try {
                    synchronized(this.groupSemaphore) {
                        this.groupSemaphore.wait(wait);
                    }
                } catch (InterruptedException ie) {}
            }
            if(!this.worker.isSetRuntimeDataComplete() && !this.workTracker.isDone()) {
                this.workTracker.cancel(true);
                abandoned=true;
                if (log.isDebugEnabled())
                    log.debug("outputRendering() : killed. " +
                            "(key="+this.groupRenderingKey.toString()+")");
            } else {
                this.groupSemaphore.waitOn();
            }
            // reset timer for rendering
            timeOutTarget=System.currentTimeMillis()+this.timeOut;
        }

        if(!abandoned) {
            try {
                this.workTracker.get(this.timeOut, TimeUnit.MILLISECONDS);
            } catch (TimeoutException te) {
                if (log.isDebugEnabled()) {
                    log.debug("outputRendering() : channel [" + this.channel + "] timed out", te);
                }
            } catch (CancellationException ce) {

                if (log.isDebugEnabled()) {
                    Throwable t = null;
                    try {
                        // in a try block to ensure further errors don't block reporting
                        // the CancellationException.
                        t = this.worker.getException();
                    } catch (Exception e) {
                        // ignore problem in getting the exception to report.
                    }
                    log.debug("outputRendering() : channel [" + this.channel + "] threw an exception [" + t + "] and so its task was cancelled.");
                }

            } catch (Exception e) {
                // no matter what went wrong (CancellationException, a NullPointerException, etc.)
                // the recovery code following this attempt to get the result from the workTracker Future
                // should be allowed to run.
                log.error("Unexpected exceptional condition trying to get the result from the workTracker Future rendering channel [" + this.channel + "].", e);
            }

            if(!this.workTracker.isDone()) {
                this.workTracker.cancel(true);
                abandoned=true;
                if (log.isDebugEnabled())
                    log.debug("outputRendering() : killed.");
            } else {
                boolean successful = this.workTracker.isDone() && !this.workTracker.isCancelled() && this.worker.getException() == null;
                abandoned=!successful;
            }

        }

        if (!abandoned && this.worker.done ()) {
            if (this.worker.successful() && ((this.rd != null && RequestType.ACTION.equals(this.rd.getRequestType())) || this.worker.getBuffer() != null || this.worker.getCharacters() != null)) {
                return RENDERING_SUCCESSFUL;
            }
        
            // rendering was not successful
            Throwable e;
            if((e=this.worker.getException())!=null) throw new InternalPortalException(e);
            // should never get there, unless thread.stop() has seriously messed things up for the worker thread.
            return RENDERING_FAILED;
        }
       
        Throwable e = null;
        if (this.worker != null) {
          e = this.worker.getException();
        }

        if (e != null) {
            throw new InternalPortalException(e);
        }
       
        // Assume rendering has timed out
        return RENDERING_TIMED_OUT;
    }


    /**
     * Returns rendered buffer.
     * This method does not perform any status checks, so make sure to call completeRendering() prior to invoking this method.
     *
     * @return rendered buffer
     */
    public SAX2BufferImpl getBuffer() {
        return this.worker != null ? this.worker.getBuffer() : null;
    }

    /**
     * Returns a character output of a channel rendering.
     */
    public String getCharacters() {
        if(this.worker!=null) {
            return this.worker.getCharacters();
        }

        if (log.isDebugEnabled()) {
            log.debug("getCharacters() : worker is null already !");
        }

        return null;
    }
   
    /* (non-Javadoc)
     * @see org.jasig.portal.IChannelRenderer#getRenderTime()
     */
    public long getRenderTime() {
        return this.worker.getRenderTime();
    }


    /* (non-Javadoc)
     * @see org.jasig.portal.IChannelRenderer#isRenderedFromCache()
     */
    public boolean isRenderedFromCache() {
        return this.worker.isRenderedFromCache();
    }


    /**
     * Sets a character cache for the current rendering.
     */
    public void setCharacterCache(String chars) {
        if(this.worker!=null) {
            this.worker.setCharacterCache(chars);
        }
    }

    /**
     * This method suppose to take care of the runaway rendering threads.
     * This method will be called from ChannelManager explictly.
     */
    public void kill() {
        if(this.workTracker!=null && !this.workTracker.isDone())
            this.workTracker.cancel(true);
    }

    public String toString() {

        return this.getClass().getSimpleName() + " [" +
                "channel=" + this.channel + ", " +
                "rendering=" + this.rendering + ", " +
                "doneRendering=" + this.donerendering + ", " +
                "startTime=" + this.startTime + ", " +
                "timeOut=" + this.timeOut + "]";
    }

    public String getChannelTitle() {


        if (log.isTraceEnabled()) {
            log.trace("Getting channel title for ChannelRenderer " + this);
        }

        // default to null, which indicates the ChannelRenderer doesn't have
        // a dynamic channel title available.
        String channelTitle = null;
        try {
            // block on channel rendering to allow channel opportunity to
            // provide dynamic title.
            int renderingStatus = completeRendering();
            if (renderingStatus == RENDERING_SUCCESSFUL) {
                channelTitle = this.worker.getChannelTitle();
            }
        } catch (Throwable t) {
            log.error("Channel rendering failed while getting title for channel renderer " + this, t);
        }

        // will be null indicating no dynamic title unless successfully obtained title.
        return channelTitle;

    }


    protected class Worker extends BaseTask implements IWorker {
        private final IChannel channel;
        private final ChannelRuntimeData rd;
        private final Map<TrackingThreadLocal<Object>, Object> currentData;
        private final RequestAttributes requestAttributes;
        private final Locale locale;
       
        private boolean successful;
        private boolean done;
        private boolean setRuntimeDataComplete;
        private SAX2BufferImpl buffer;
        private String cbuffer;
       
        private long renderTime;
        private boolean renderedFromCache = false;

        /**
         * The dynamic title of the channel, if any.  Null otherwise.
         */
        private String channelTitle = null;

        public Worker(IChannel ch, ChannelRuntimeData runtimeData) {
            this.channel = ch;
            this.rd = runtimeData;
            this.requestAttributes = RequestContextHolder.getRequestAttributes();
            this.locale = LocaleContextHolder.getLocale();

            successful = false;
            done = false;
            setRuntimeDataComplete = false;
            buffer = null;
            cbuffer = null;
            currentData = TrackingThreadLocal.getCurrentData();
           
            if (log.isTraceEnabled()) {
                log.trace("Created " + this.toString());
            }
        }

        public boolean isSetRuntimeDataComplete() {
            return this.setRuntimeDataComplete;
        }

        //TODO review this for clarity
        public void execute () throws Exception {
            if (log.isTraceEnabled()) {
                log.trace("Started execution " + this.toString());
            }
           
            final long startTime = System.currentTimeMillis();
            try {
                TrackingThreadLocal.setCurrentData(this.currentData);
                RequestContextHolder.setRequestAttributes(this.requestAttributes);
                LocaleContextHolder.setLocale(this.locale);
                if (log.isDebugEnabled()) {
                    log.debug("Bound request attributes to thread: " + this.requestAttributes);
                }
               
                if(rd!=null) {
                    channel.setRuntimeData(rd);
                   
                    if (RequestType.ACTION.equals(rd.getRequestType())) {
                        if (channel instanceof IPortletAdaptor) {
                            try {
                                ((IPortletAdaptor)channel).processAction();
                                successful = true;
                            }
                            catch (Exception e) {
                                this.setException(e);
                            }
                        }
                        else {
                            this.setException(new ClassCastException("Action request for channel '" + channel + "' that does not implement '" + IPortletAdaptor.class + "'"));
                        }

                        done = true;

                        return;
                    }
                }
                setRuntimeDataComplete=true;
               
                if(groupSemaphore!=null) {
                    groupSemaphore.checkInAndWaitOn(groupRenderingKey);
                }

                if(CACHE_CHANNELS) {
                    // try to obtain rendering from cache
                    if(channel instanceof ICacheable ) {
                        final CacheFactory cacheFactory = CacheFactoryLocator.getCacheFactory();
                        final Map<Serializable, ChannelCacheEntry> systemCache = cacheFactory.getCache(SYSTEM_WIDE_CHANNEL_CACHE);
                       
                        ChannelCacheKey key=((ICacheable)channel).generateKey();
                        if (log.isTraceEnabled()) {
                            log.trace("Generated cache key " + (key != null ? key.getKey() : null) + " for worker " + this.toString());
                        }
                        if(key!=null) {
                            if(key.getKeyScope()==ChannelCacheKey.SYSTEM_KEY_SCOPE) {
                                ChannelCacheEntry entry=systemCache.get(key.getKey());
                                if(entry!=null) {
                                    // found cached page
                                    // check page validity
                                    if(((ICacheable)channel).isCacheValid(entry.validity) && (entry.buffer!=null)) {
                                        // use it
                                        if(ccacheable && (entry.buffer instanceof String)) {
                                            cbuffer=(String)entry.buffer;
                                            if (log.isDebugEnabled()) {
                                                log.debug("retrieved system-wide cached character content based on a key \""+key.getKey()+"\"");
                                            }
                                        } else if(entry.buffer instanceof SAX2BufferImpl) {
                                            buffer=(SAX2BufferImpl) entry.buffer;
                                            if (log.isDebugEnabled()) {
                                                log.debug("retrieved system-wide cached content based on a key \""+key.getKey()+"\"");
                                            }
                                        }
                                        this.channelTitle = entry.title;
                                       
                                        this.renderedFromCache = true;
                                    } else {
                                        // remove it
                                        systemCache.remove(key.getKey());
                                        if (log.isDebugEnabled()) {
                                            log.debug("removed system-wide unvalidated cache based on a key \""+key.getKey()+"\"");
                                        }
                                    }
                                }
                            } else {
                                // by default we assume INSTANCE_KEY_SCOPE
                                ChannelCacheEntry entry=getChannelCache().get(key.getKey());
                                if(entry!=null) {
                                    // found cached page
                                    // check page validity
                                    if(((ICacheable)channel).isCacheValid(entry.validity) && (entry.buffer!=null)) {
                                        // use it
                                        if(ccacheable && (entry.buffer instanceof String)) {
                                            cbuffer=(String)entry.buffer;
                                            if (log.isDebugEnabled()) {
                                                log.debug("retrieved instance-cached character content based on a key \""+key.getKey()+"\" by " + this.toString());
                                            }

                                        } else if(entry.buffer instanceof SAX2BufferImpl) {
                                            buffer=(SAX2BufferImpl) entry.buffer;
                                            if (log.isDebugEnabled()) {
                                                log.debug("retrieved instance-cached content based on a key \""+key.getKey()+"\" by " + this.toString());
                                            }
                                        }
                                        this.channelTitle = entry.title;
                                       
                                        this.renderedFromCache = true;
                                    } else {
                                        // remove it
                                        getChannelCache().remove(key.getKey());
                                        if (log.isDebugEnabled()) {
                                            log.debug("removed unvalidated instance-cache based on a key \""+key.getKey()+"\" by " + this.toString());
                                        }
                                    }
                                }
                            }
                        }

                        // future work: here we should synchronize based on a particular cache key.
                        // Imagine a VERY popular cache entry timing out, then portal will attempt
                        // to re-render the page in many threads (serving many requests) simultaneously.
                        // If one was to synchronize on writing cache for a particular key, one thread
                        // would render and others would wait for it to complete.

                        // check if need to render
                        if(cbuffer==null && buffer==null) {
                            if (channel instanceof ICharacterChannel) {
                                StringWriter sw = new StringWriter(100);
                                PrintWriter pw = new PrintWriter(sw);
                                ((ICharacterChannel)channel).renderCharacters(pw);
                               
                                processChannelRuntimeProperties();

                                pw.flush();
                                cbuffer = sw.toString();
                                // save cache
                                if (ccacheable && key != null) {
                                    if (key.getKeyScope() == ChannelCacheKey.SYSTEM_KEY_SCOPE) {
                                        systemCache.put(key.getKey(), new ChannelCacheEntry(cbuffer, this.channelTitle, key.getKeyValidity()));
                                        if (log.isDebugEnabled()) {
                                            log.debug("recorded system character cache based on a key \"" + key.getKey() + "\" by " + this.toString());
                                        }
                                    } else {
                                        getChannelCache().put(key.getKey(), new ChannelCacheEntry(cbuffer, this.channelTitle, key.getKeyValidity()));
                                        if (log.isDebugEnabled()) {
                                            log.debug("recorded instance character cache based on a key \"" + key.getKey() + "\" by " + this.toString());
                                        }
                                    }
                                }
                            } else {
                                // need to render again and cache the output
                                buffer = new SAX2BufferImpl ();
                                buffer.startBuffering();
                                channel.renderXML(buffer);

                                processChannelRuntimeProperties();

                                // save cache
                                if(key!=null) {

                                    if(key.getKeyScope()==ChannelCacheKey.SYSTEM_KEY_SCOPE) {
                                        systemCache.put(key.getKey(),new ChannelCacheEntry(buffer, this.channelTitle, key.getKeyValidity()));
                                        if (log.isDebugEnabled()) {
                                            log.debug("recorded system cache based on a key \""+key.getKey()+"\"");
                                        }
                                    } else {
                                        getChannelCache().put(key.getKey(),new ChannelCacheEntry(buffer, this.channelTitle, key.getKeyValidity()));
                                        if (log.isDebugEnabled()) {
                                            log.debug("recorded instance cache based on a key \""+key.getKey()+"\"");
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        if (ccacheable && channel instanceof ICharacterChannel) {
                            StringWriter sw = new StringWriter(100);
                            PrintWriter pw = new PrintWriter(sw);
                            ((ICharacterChannel)channel).renderCharacters(pw);
                            pw.flush();
                            cbuffer = sw.toString();
                        } else {
                            buffer = new SAX2BufferImpl ();
                            buffer.startBuffering();
                            channel.renderXML(buffer);
                        }
                    }
                } else  {
                    // in the case when channel cache is not enabled
                    buffer = new SAX2BufferImpl ();
                    buffer.startBuffering();
                    channel.renderXML (buffer);
                }
                successful = true;
            } catch (Exception e) {
                if(groupSemaphore!=null) {
                    groupSemaphore.checkIn(groupRenderingKey);
                }
                this.setException(e);
            }
            finally {
                TrackingThreadLocal.clearCurrentData(this.currentData.keySet());
                RequestContextHolder.resetRequestAttributes();
                LocaleContextHolder.resetLocaleContext();
               
                this.renderTime = System.currentTimeMillis() - startTime;
            }

            done = true;
           
            if (log.isTraceEnabled()) {
                log.trace("Completed execution " + this.toString());
            }
        }

        /**
         * Query the channel for ChannelRuntimePRoperties and process those
         * properties.
         *
         * Currently, only handles the optional {@link IChannelTitle} interface.
         */
        private void processChannelRuntimeProperties() {
            ChannelRuntimeProperties channelProps = this.channel.getRuntimeProperties();

            if (channelProps != null) {
                if (channelProps instanceof IChannelTitle) {

                    this.channelTitle = ((IChannelTitle) channelProps).getChannelTitle();
                    if (log.isTraceEnabled()) {
                        log.trace("Read title [" + this.channelTitle + "] by " + this.toString());
                    }
                } else {
                    if (log.isTraceEnabled()) {
                        log.trace("ChannelRuntimeProperties were non-null but did not implement ITitleable.");
                    }
                }
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("ChannelRuntimeProperties were null from channel " + channel);
                }
            }
        }

        public boolean successful () {
            return this.successful;
        }

        public SAX2BufferImpl getBuffer() {
            return this.buffer;
        }

        /**
         * Returns a character output of a channel rendering.
         */
        public String getCharacters() {
            if(!ccacheable) {
                log.error("Attempting to obtain character data for '" + channel + "' while character caching is not enabled !", new Throwable());
                return null;
            }

            if (log.isTraceEnabled()) {
                log.trace("Getting channel characters (" + (this.cbuffer != null ? this.cbuffer.length() : 0) + ") for " + this);
            }
           
            return this.cbuffer;
        }


        /**
         * Sets a character cache for the current rendering.
         */
        public void setCharacterCache(String chars) {
            cbuffer=chars;
            if(CACHE_CHANNELS) {
                // try to obtain rendering from cache
                if(channel instanceof ICacheable ) {
                    final CacheFactory cacheFactory = CacheFactoryLocator.getCacheFactory();
                    final Map<Serializable, ChannelCacheEntry> systemCache = cacheFactory.getCache(SYSTEM_WIDE_CHANNEL_CACHE);
                    ChannelCacheKey key=((ICacheable)channel).generateKey();
                    if(key!=null) {
                        if (log.isDebugEnabled()) {
                            log.debug("setCharacterCache() : called on a key \""+key.getKey()+"\"");
                        }
                        ChannelCacheEntry entry=null;
                        if(key.getKeyScope()==ChannelCacheKey.SYSTEM_KEY_SCOPE) {
                            entry=systemCache.get(key.getKey());
                            if(entry==null) {
                                if (log.isDebugEnabled()) {
                                    log.debug("setCharacterCache() : setting character cache buffer based on a system key \""+key.getKey()+"\"");
                                }
                                entry=new ChannelCacheEntry(chars, this.channelTitle, key.getKeyValidity());
                            } else {
                                entry.buffer=chars;
                                entry.title=this.channelTitle;
                            }
                            systemCache.put(key.getKey(),entry);
                        } else {
                            // by default we assume INSTANCE_KEY_SCOPE
                            entry=getChannelCache().get(key.getKey());
                            if(entry==null) {
                                if (log.isDebugEnabled()) {
                                    log.debug("setCharacterCache() : no existing cache on a key \""+key.getKey()+"\"");
                                }
                                entry=new ChannelCacheEntry(chars, this.channelTitle, key.getKeyValidity());
                            } else {
                                entry.buffer=chars;
                                entry.title=this.channelTitle;
                            }
                            getChannelCache().put(key.getKey(),entry);
                        }
                    } else {
                        if (log.isDebugEnabled()) {
                            log.debug("setCharacterCache() : channel cache key is null.");
                        }
                    }
                }
            }
        }

        public boolean done () {
            return this.done;
        }

        /**
         * Get a Sring representing the dynamic channel title reported by the
         * IChannel this ChannelRenderer rendered.  Returns null if the channel
         * reported no such title or the channel isn't done rendering.
         *
         * @return dynamic channel title, or null if no title available.
         */
        public String getChannelTitle() {

            if (log.isTraceEnabled()) {
                log.trace("Getting channel title (" + this.channelTitle + ") for " + this);
            }

            // currently, just provides no dynamic title if not done rendering
            if (this.done) {
                return this.channelTitle;
            } else {
                return null;
            }
        }

        /**
         * @return the renderTime
         */
        public long getRenderTime() {
            return renderTime;
        }

        /**
         * @return the renderedFromCache
         */
        public boolean isRenderedFromCache() {
            return renderedFromCache;
        }

        @Override
        public String toString() {
            return this.getClass().getSimpleName() + " [" +
                "channel=" + this.channel + ", " +
                "done=" + this.done + ", " +
                "successful=" + this.successful + ", " +
                "renderTime=" + this.renderTime + ", " +
                "renderedFromCache=" + this.renderedFromCache + ", " +
                "cbuffer.length=" + (this.cbuffer != null ? this.cbuffer.length() : 0) + ", " +
                "parent=" + ChannelRenderer.this + "]";
        }
    }
   
    protected interface IWorker extends Task {

        public boolean isSetRuntimeDataComplete();

        //TODO review this for clarity
        public void execute() throws Exception;

        public boolean successful();

        public SAX2BufferImpl getBuffer();

        /**
         * Returns a character output of a channel rendering.
         */
        public String getCharacters();

        /**
         * Sets a character cache for the current rendering.
         */
        public void setCharacterCache(String chars);

        public boolean done();

        /**
         * @return the renderTime
         */
        public long getRenderTime();

        /**
         * @return the renderedFromCache
         */
        public boolean isRenderedFromCache();

        /**
         * Get a Sring representing the dynamic channel title reported by the
         * IChannel this ChannelRenderer rendered.  Returns null if the channel
         * reported no such title or the channel isn't done rendering.
         *
         * @return dynamic channel title, or null if no title available.
         */
        public String getChannelTitle();
    }
}
TOP

Related Classes of org.jasig.portal.ChannelRenderer$IWorker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.