Package de.innovationgate.wgpublisher.webtml

Source Code of de.innovationgate.wgpublisher.webtml.Range

/*******************************************************************************
* Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved.
*
* This file is part of the OpenWGA server platform.
*
* OpenWGA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, a special exception is granted by the copyright holders
* of OpenWGA called "OpenWGA plugin exception". You should have received
* a copy of this exception along with OpenWGA in file COPYING.
* If not, see <http://www.openwga.com/gpl-plugin-exception>.
*
* OpenWGA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenWGA in file COPYING.
* If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package de.innovationgate.wgpublisher.webtml;

import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import de.innovationgate.utils.cache.CacheException;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGDatabase;
import de.innovationgate.wgpublisher.WGACore;
import de.innovationgate.wgpublisher.expressions.ExpressionEngine;
import de.innovationgate.wgpublisher.expressions.ExpressionEngineFactory;
import de.innovationgate.wgpublisher.expressions.ExpressionResult;
import de.innovationgate.wgpublisher.webtml.utils.TMLException;
import de.innovationgate.wgpublisher.webtml.utils.TMLOption;

public class Range extends Base {

    private String cachekey;
    private String cachelatency;

    private String exprlanguage;
   
    private String waittimeout;

    private String defaultxpl;
   
    private String labelcontainer;
    private String labelfile;
   
    private String tmlscope;


    public static class Status extends BaseTagStatus {
        private String _currentCacheKey;
        private String _currentCacheId;
        private int cacheLatency = 0;
   
        private boolean putCache;
        private boolean servedStale = false;
        private Date cacheDate;
       
        @Override
        public Object getTagInfo(String name) throws WGAPIException {
            if (name.equals("cachekey")) {
                return this._currentCacheKey;
            }
            else if (name.equals("cacheused")) {
                return new Boolean(!this.putCache);
            }
            else if (name.equals("cachestale")) {
                return new Boolean(this.servedStale);
            }
           
            return super.getTagInfo(name);
        }
    }
   
    @Override
    protected BaseTagStatus createTagStatus() {
        return new Status();
    }


    private static Map currentlyEvaluatedCaches = Collections.synchronizedMap(new HashMap());

   
   
    public void tmlEndTag() throws TMLException {

        Status status = (Status) getStatus();
        if (status.putCache == true) {
            try {
                int latency = 0;
                getCore().getWebTMLCache().putCache(getTMLContext().db().getDbReference(), status._currentCacheId, status._currentCacheKey, this.getResultString(), status.cacheDate, status.cacheLatency);
            }
            catch (CacheException e) {
                getTMLContext().getlog().error("Exception writing WebTML cache data for key " + getTMLContext().db().getDbReference() + "/" + status._currentCacheId + "/" + status._currentCacheKey, e);
            }
        }
             
   }

    /**
     * @see de.innovationgate.wgpublisher.webtml.Base#tmlStartTag()
     */
    public void tmlStartTag() throws TMLException {

        Status status = (Status) getStatus();
       
        String latencyStr = getCachelatency();
        if (!WGUtils.isEmpty(latencyStr)) {
            status.cacheLatency = stringToInteger(latencyStr, 0) * 60;
        }
       
        // Initialize
        status._currentCacheKey = null;
        status._currentCacheId = getId();
        status.putCache = false;
        status.cacheDate = null;
        status.servedStale = false;

        // Set default xpl
        String xpl = this.getDefaultxpl();
        if (xpl != null) {
            status.setOption(Base.OPTION_DEFAULT_XPLANGUAGE, xpl, null);
        }
       
        // Set label information
        String labelContainer = getLabelcontainer();
        if (labelContainer != null) {
            status.setOption(Base.OPTION_DEFAULT_LABELCONTAINER + getTMLContext().getDesignDBKey(), labelContainer, null);
        }
       
        String labelFile = getLabelfile();
        if (labelFile != null) {
            status.setOption(Base.OPTION_DEFAULT_LABELFILE + getTMLContext().getDesignDBKey(), labelFile, null);
        }
       
        // Set scope information
        String scope = getTmlscope();
        if (scope != null) {
            status.setOption(Base.OPTION_WEBTML_SCOPE, scope, TMLOption.SCOPE_GLOBAL);
        }

        // WebTML Cache functionality
        String currentCacheby = this.getCachekey();
        if (currentCacheby != null) {

            if (status._currentCacheId == null) {
                this.addWarning("To use cache, the range tag must have a unique id");
                return;
            }

            // Eval cache key
            if (!currentCacheby.equals("")) {
                ExpressionEngine engine = ExpressionEngineFactory.getEngine(this.getExprlanguage());
                ExpressionResult result = engine.evaluateExpression(currentCacheby, this.getTMLContext(), ExpressionEngine.TYPE_EXPRESSION, null);
                if (result.isError()) {
                    addExpressionWarning(currentCacheby, result);
                    return;
                }
                status._currentCacheKey = String.valueOf(result.getResult());
            }
            else {
                status._currentCacheKey = "";
            }
           
            // In browser interface we add a custom value to the cache key, so BI caches and Non-Bi caches differ (B000059DA)
            if (status.isBrowserInterface()) {
                status._currentCacheKey = status._currentCacheKey + "###BROWSER-INTERFACE";
            }

            WGDatabase db = this.getTMLContext().content().getDatabase();

            // Look if the current cache is in evaluation right now, wait for 10 seconds
            int sleepTimes = 0;      
            String cacheLockKey = buildCacheLockKey();
            int waitTimeout = 60;
            try {
                waitTimeout = Integer.parseInt(getWaittimeout());
            }
            catch (NumberFormatException e) {
                addWarning("Cannot parse waittimeout as number: " + getWaittimeout());
            }
           
            if (currentlyEvaluatedCaches.containsKey(cacheLockKey)) {
               
                // If we are allowed to serve stale data while the cache processes, we serve the current content of the cache
                boolean serveStaleData = db.getBooleanAttribute(WGACore.DBATTRIB_WEBTMLCACHE_SERVESTALEDATA, true);
                if (serveStaleData) {
                    String content = null;
                    try {
                        content = getCore().getWebTMLCache().getCache(db.getDbReference(), status._currentCacheId, status._currentCacheKey, new Date(Long.MIN_VALUE));
                    }
                    catch (CacheException e) {
                        getTMLContext().getlog().error("Exception retrieving WebTML cache data for key " + getTMLContext().db().getDbReference() + "/" + status._currentCacheId + "/" + status._currentCacheKey, e);
                    }
                    if (content != null) {
                       
                        // Tell all ranges above us to have a cache latency of 1 minute, so cached stale data is updated after this time
                        Range.Status ancestor = status;
                        while ((ancestor = (Range.Status) ancestor.getAncestorTag(Range.class)) != null) {
                            ancestor.cacheLatency = 1;
                        }
                       
                        status._currentCacheKey = null;
                        status._currentCacheId = null;
                        status.servedStale = true;
                        this.setEvalBody(false);
                        this.setResult(content);
                        return;
                    }
                }
               
                // Else we wait until cache calculation is completed or we run into the timeout
                while (currentlyEvaluatedCaches.containsKey(cacheLockKey)) {
                   
                    Long cacheTime = (Long) currentlyEvaluatedCaches.get(cacheLockKey);
                    if( cacheTime != null ){
                    long cacheTimeout = cacheTime.longValue() + (1000 * 60 * 15);
                      if (System.currentTimeMillis() > cacheTimeout) {
                          log.warn("Former cache creation of key '" + status._currentCacheKey + "' took more than 15 minutes. Removing synchronisation lock now");
                          currentlyEvaluatedCaches.remove(cacheLockKey);
                          break;
                      }
                    }
                    try {
                        Thread.sleep(1000);
                    }
                    catch (InterruptedException e) {
                    }
                    sleepTimes++;
                    if (sleepTimes > waitTimeout) {
                        log.warn("Waiting for cache creation of key '" + cacheLockKey + "' timed out after " + waitTimeout + " second(s). Tag is canceled.");
                        this.addWarning("Waiting for cache creation of key '" + cacheLockKey + "' timed out after " + waitTimeout + " second(s). Tag is canceled.", true);
                        this.setEvalBody(false);
                        this.setCancelTag(true);
                        this.setResult("This section is currently recalculated. Please try again later.");
                        return;
                    }
                }
            }
           
            currentlyEvaluatedCaches.put(cacheLockKey, new Long(System.currentTimeMillis()));

         
            // Try to retrieve cache
            Date testedDate = getCore().getDeployer().getLastChangedOrDeployed(db);
            String content = null;
            try {
                content = getCore().getWebTMLCache().getCache(db.getDbReference(), status._currentCacheId, status._currentCacheKey, testedDate);
            }
            catch (CacheException e) {
                getTMLContext().getlog().error("Exception retrieving WebTML cache data for key "  + getTMLContext().db().getDbReference() + "/" + status._currentCacheId + "/" + status._currentCacheKey, e);
            }
           
            if (content != null) {
                Range.currentlyEvaluatedCaches.remove(cacheLockKey);
                status._currentCacheId = null;
                this.setEvalBody(false);
                this.setResult(content);
            }
            else {
                status.putCache = true;
                status.cacheDate = testedDate;
                if (log.isDebugEnabled()) {
                    log.debug("Trying to evaluate cache for key '" + cacheLockKey + "'");
                }
            }
        }
    }

    private String buildCacheLockKey() {
        Status status = (Status) getStatus();
        return getTMLContext().db().getDbReference() + "//" + status._currentCacheId + "//" + status._currentCacheKey;
    }
   
    public static void clearCacheLocks() {
        Range.currentlyEvaluatedCaches.clear();
    }

    /**
     * Returns the cacheby.
     *
     * @return String
     */
    public String getCachekey() {
        return this.getTagAttributeValue("cachekey", cachekey, null);
    }

    /**
     * Sets the cacheby.
     *
     * @param cacheby
     *            The cacheby to set
     */
    public void setCachekey(String cacheby) {
        this.cachekey = cacheby;
    }

    /**
     * Returns the exprlanguage.
     *
     * @return String
     */
    public String getExprlanguage() {
        return this.getTagAttributeValue("exprlanguage", exprlanguage, this.getDefaultExpressionLanguage());
    }

    public String getXplanguage() {
        return this.getExprlanguage();
    }

    public void setXplanguage(String xpl) {
        this.setExprlanguage(xpl);
    }

    /**
     * Sets the exprlanguage.
     *
     * @param exprlanguage
     *            The exprlanguage to set
     */
    public void setExprlanguage(String exprlanguage) {
        this.exprlanguage = exprlanguage;
    }

    /**
     * Returns the defaultxpl.
     *
     * @return String
     */
    public String getDefaultxpl() {
        return this.getTagAttributeValue("defaultxpl", defaultxpl, null);
    }

    /**
     * Sets the defaultxpl.
     *
     * @param defaultxpl
     *            The defaultxpl to set
     */
    public void setDefaultxpl(String defaultxpl) {
        this.defaultxpl = defaultxpl;
    }

    /**
     * @return Returns the labelcontainer.
     */
    public String getLabelcontainer() {
        return getTagAttributeValue("labelcontainer", labelcontainer, null);
    }

    /**
     * @param labelcontainer The labelcontainer to set.
     */
    public void setLabelcontainer(String labelcontainer) {
        this.labelcontainer = labelcontainer;
    }

    /**
     * @return Returns the labelfile.
     */
    public String getLabelfile() {
        return getTagAttributeValue("labelfile", labelfile, null);
    }

    /**
     * @param labelfile The labelfile to set.
     */
    public void setLabelfile(String labelfile) {
        this.labelfile = labelfile;
    }

    public static Map getCurrentlyEvaluatedCaches() {
        return currentlyEvaluatedCaches;
    }

    protected void tmlCleanup() {
       
        Status status = (Status) getStatus();
        if (status.putCache == true && status._currentCacheKey != null) {
            Long time = (Long) Range.currentlyEvaluatedCaches.remove(buildCacheLockKey());
            if (time == null) {
                addWarning("Could not remove cache lock key '" + buildCacheLockKey() + "' because it was not registered anymore");
            }
        }
       
    }

    public String getWaittimeout() {
        return getTagAttributeValue("waittimeout", waittimeout, "60");
    }

    public void setWaittimeout(String waittimeout) {
        this.waittimeout = waittimeout;
    }

    public String getCachelatency() {
        return getTagAttributeValue("cachelatency",cachelatency, null);
    }

    public void setCachelatency(String cachelatency) {
        this.cachelatency = cachelatency;
    }

    public String getTmlscope() {
        return getTagAttributeValue("tmlscope", tmlscope, null);
    }

    public void setTmlscope(String tmlscope) {
        this.tmlscope = tmlscope;
    }

}
TOP

Related Classes of de.innovationgate.wgpublisher.webtml.Range

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.