ResponderCache.java :  » Ajax » Laszlo-4.0.10 » org » openlaszlo » servlets » responders » Java Open Source

Java Open Source » Ajax » Laszlo 4.0.10 
Laszlo 4.0.10 » org » openlaszlo » servlets » responders » ResponderCache.java
/******************************************************************************
 * ResponderCache.java
 * ****************************************************************************/

/* J_LZ_COPYRIGHT_BEGIN *******************************************************
* Copyright 2001-2007 Laszlo Systems, Inc.  All Rights Reserved.              *
* Use is subject to license terms.                                            *
* J_LZ_COPYRIGHT_END *********************************************************/

package org.openlaszlo.servlets.responders;

import java.io.*;
import java.net.UnknownHostException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;
import java.util.HashMap;
import java.util.Iterator;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletOutputStream;
import org.openlaszlo.cache.RequestCache;
import org.openlaszlo.data.*;
import org.openlaszlo.media.MimeType;
import org.openlaszlo.server.LPS;
import org.openlaszlo.utils.LZHttpUtils;
import org.openlaszlo.utils.ChainedException;
import org.openlaszlo.xml.internal.XMLUtils;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
import org.apache.log4j.Logger;

public abstract class ResponderCache extends Responder
{
    private static boolean mIsInitialized = false;

    private static HashMap mDataSourceMap = new HashMap();
    private static DataSource mHTTPDataSource = null;

    private static Logger mLogger = Logger.getLogger(ResponderCache.class);

    protected RequestCache mCache = null;

    /**
     * Keeps track of url stats.
     */
    public class URLStat
    {
        String mName;

        final static public int ERRTYPE_NONE             = -1;
        final static public int ERRTYPE_CONVERSION       = 0;
        final static public int ERRTYPE_DATA_SOURCE      = 1;
        final static public int ERRTYPE_UNKNOWN_HOST     = 2;
        final static public int ERRTYPE_MALFORMED_URL    = 3;
        final static public int ERRTYPE_IO               = 4;
        final static public int ERRTYPE_ILLEGAL_ARGUMENT = 5;
        final static public int ERRTYPE_TIMEOUT          = 6;
        final static public int ERRTYPE_FORBIDDEN        = 7;
        final static public int ERRTYPE_OTHER            = 8;
        final static public int NUM_ERRTYPES             = 9;


        HashMap mURLs = new HashMap();
        HashMap mErrorURLs = new HashMap();

        int mSuccessCount;
        /**
         * 0: mConversionException, 1: mDataSourceException, 2: mUnknownHostException,
         * 3: mMalformedURLException, 4: mIOException, 5: mIllegalArgumentException,
         * 6: mInterrupedIOException, 7 mException
         */
        int[] mErrorCount = new int[NUM_ERRTYPES];

        
        boolean mDoURLCollection = false;
        

        /**
         * Create an URLStat with a name.
         */
        public URLStat(String name)
        {
            mName = name;
            clear();
        }
        
        /**
         * @param doCollection whether the object should collect unique url
         * info.
         */
        void doURLCollection(boolean doCollection)
        {
            if (mDoURLCollection != doCollection) {
                mDoURLCollection = doCollection;
                clear();
            }
        }

        /**
         * Increment stat on url with successful status.
         * @param url successful url.
         */
        void success(String url)
        {
            int x = url.indexOf('?');
            if (x != -1) {
                url = url.substring(0, x);
            }
            synchronized (mURLs) {
                if (mDoURLCollection) {
                    // Add unique urls
                    int[] s = (int[]) mURLs.get(url);
                    if (s == null) {
                        s = new int[1];
                        mURLStat.mURLs.put(url, s);
                    }
                    ++s[0];
                }
                ++mSuccessCount;
            }
        }


        /**
         * Increment stat on url with error status.
         *
         * @param errType see ERRTYPEs.
         * @param url error url.
         */
        void error(int errType, String url)
        {
            int x = url.indexOf('?');
            if (x != -1) {
                url = url.substring(0, x);
            }
            synchronized (mErrorURLs) {
                if (mDoURLCollection) {
                    int[] e = (int[]) mErrorURLs.get(url);
                    if (e == null) {
                        e = new int[NUM_ERRTYPES];
                        mErrorURLs.put(url, e);
                    }
                    ++e[errType];
                }
                ++mErrorCount[errType];
            }

        }

        /**
         * Clear URL stats.
         */
        void clear()
        {
            synchronized (mURLs) {
                mURLs.clear();
                mSuccessCount = 0;
            }

            synchronized (mErrorURLs) {
                mErrorURLs.clear();
                for (int i=0; i < mErrorCount.length; i++)
                    mErrorCount[i] = 0;
            }
        }

        /**
         * Return url statistics in XML string.
         * @return xml info.
         */
        public String toXML()
        {
            StringBuffer buf = new StringBuffer();
            synchronized (mURLs) {
                buf.append("<").append(mName).append("-urls ")
                    .append(" unique=\"").append(mURLs.size()).append("\"")
                    .append(">\n");

                buf.append("<success")
                    .append(" total-requests=\"").append(mSuccessCount).append("\"")
                    .append(">\n");
                if (mDoURLCollection)
                {
                    Iterator iter = mURLs.keySet().iterator();
                    while (iter.hasNext()) {
                        String k = (String)iter.next();
                        int[] success = (int[])mURLs.get(k);
                        buf.append("<url")
                            .append(" requests=\"").append(success[0]).append("\"")
                            .append(" href=\"").append(XMLUtils.escapeXml(k)).append("\" />");
                    }
                }
                buf.append("</success>\n");
            }

            synchronized (mErrorURLs) {
                int errTotal = 0;
                for (int i=0; i < mErrorCount.length; i++)
                    errTotal += mErrorCount[i];
                buf.append("<errors")
                    .append(" total-errors=\"").append(errTotal).append("\"")
                    .append(" conversion=\"").append(mErrorCount[ERRTYPE_CONVERSION]).append("\"")
                    .append(" datasource=\"").append(mErrorCount[ERRTYPE_DATA_SOURCE]).append("\"")
                    .append(" unknownhost=\"").append(mErrorCount[ERRTYPE_UNKNOWN_HOST]).append("\"")
                    .append(" malformedurl=\"").append(mErrorCount[ERRTYPE_MALFORMED_URL]).append("\"")
                    .append(" ioexception=\"").append(mErrorCount[ERRTYPE_IO]).append("\"")
                    .append(" illegalargument=\"").append(mErrorCount[ERRTYPE_ILLEGAL_ARGUMENT]).append("\"")
                    .append(" timeout=\"").append(mErrorCount[ERRTYPE_TIMEOUT]).append("\"")
                    .append(" forbidden=\"").append(mErrorCount[ERRTYPE_FORBIDDEN]).append("\"")
                    .append(" uncaught-exception=\"").append(mErrorCount[ERRTYPE_OTHER]).append("\"")
                    .append(">\n");
                if (mDoURLCollection)
                {
                    Iterator iter = mErrorURLs.keySet().iterator();
                    while (iter.hasNext()) {
                        String k = (String)iter.next();
                        int[] e = (int[])mErrorURLs.get(k);
                        buf.append("<url")
                            .append(" conversion=\"").append(e[ERRTYPE_CONVERSION]).append("\"")
                            .append(" datasource=\"").append(e[ERRTYPE_DATA_SOURCE]).append("\"")
                            .append(" unknownhost=\"").append(e[ERRTYPE_UNKNOWN_HOST]).append("\"")
                            .append(" malformedurl=\"").append(e[ERRTYPE_MALFORMED_URL]).append("\"")
                            .append(" ioexception=\"").append(e[ERRTYPE_IO]).append("\"")
                            .append(" illegalargument=\"").append(e[ERRTYPE_ILLEGAL_ARGUMENT]).append("\"")
                            .append(" timeout=\"").append(e[ERRTYPE_TIMEOUT]).append("\"")
                            .append(" forbidden=\"").append(e[ERRTYPE_FORBIDDEN]).append("\"")
                            .append(" uncaught-exception=\"").append(e[ERRTYPE_OTHER]).append("\"")
                            .append(" href=\"").append(XMLUtils.escapeXml(k)).append("\"")
                            .append(" />\n");
                    }
                }
                buf.append("</errors>\n");
            }

            buf.append("</").append(mName).append("-urls>\n");

            return buf.toString();
        }
    }

    public URLStat mURLStat = null;

    synchronized public void init(String reqName, ServletConfig config, 
            RequestCache cache, Properties prop)
        throws ServletException, IOException
    {
        super.init(reqName, config, prop);

        String reqProp = reqName.toLowerCase() + "Request.collectURL";
        boolean doURLCollection =
            prop.getProperty(reqProp, "false").intern() == "true";

        mURLStat = new URLStat(reqName);
        mURLStat.doURLCollection(doURLCollection);

        if (! mIsInitialized) {
            //------------------------------------------------------------
            // Well-known data sources
            //------------------------------------------------------------
            mHTTPDataSource   = new HTTPDataSource();

            mDataSourceMap.put("http",   mHTTPDataSource);

            // For security reasons, we are now mapping file => http 
            mDataSourceMap.put("file",   mHTTPDataSource);

            /*
            try {
                mDataSourceMap.put("file",   new FileDataSource());
            } catch (Throwable e) {
                mLogger.warn("can't load file datasource", e);
            }
            */

            try {
                mDataSourceMap.put("java",   new JavaDataSource());
            } catch (Throwable e) {
                mLogger.warn("can't load java datasource", e);
            }

            try {
                mDataSourceMap.put("soap-json",   new org.openlaszlo.data.json.SOAPDataSource());
            } catch (Throwable e) {
                mLogger.warn("can't load soap datasource", e);
            }

                        try {
                mDataSourceMap.put("soap-swf",   new org.openlaszlo.data.swf.SOAPDataSource());
            } catch (Throwable e) {
                mLogger.warn("can't load soap datasource", e);
            }

            try {
                mDataSourceMap.put("xmlrpc", new XMLRPCDataSource());
            } catch (Throwable e) {
                mLogger.warn("can't load xmlrpc datasource", e);
            }

            mIsInitialized = true;
        }

        mCache = cache; 
    }

    protected void respondImpl(HttpServletRequest req, HttpServletResponse res) {

        String path = req.getServletPath();
        String url;
        try {
            url  = DataSource.getURL(req);
        } catch (java.net.MalformedURLException e) {
            respondWithErrorSWF(res, "bad url: " + e.getMessage());
            if (mCollectStat) {
                mURLStat.error(URLStat.ERRTYPE_MALFORMED_URL, "bad-url");
            }
            return;
        }

        if (path.endsWith(".lzo")) {
            path = path.substring(0, path.length() - 1) + "x";
        }

        if (req.getMethod().intern() == "POST") {
            float fpv = getFlashPlayerVersion(req);
            String ua = req.getHeader(LZHttpUtils.USER_AGENT);
            mLogger.debug(
/* (non-Javadoc)
 * @i18n.test
 * @org-mes="POST request, flash player version: " + p[0]
 */
                        org.openlaszlo.i18n.LaszloMessages.getMessage(
                                ResponderCache.class.getName(),"051018-328", new Object[] {new Float(fpv)})
);
            if (fpv < 6.47 && 
                LPS.configuration.optionAllows("disable-post-keep-alive", ua)) {
                // Prevent browser keep-alive to get around bug 4048.
                mLogger.debug(
/* (non-Javadoc)
 * @i18n.test
 * @org-mes="Disabling keep-alive for " + p[0]
 */
                        org.openlaszlo.i18n.LaszloMessages.getMessage(
                                ResponderCache.class.getName(),"051018-339", new Object[] {ua})
);
                res.setHeader("Connection", "close");
                res.setHeader("Keep-Alive", "close");
            }
        }

        if ( ! LPS.configuration.optionAllows(path, "proxy-security-urls", url) ) {
            String err = "Forbidden url: " +  url;
            respondWithError(res, err, HttpServletResponse.SC_FORBIDDEN);
            mLogger.error(err);
            if (mCollectStat) {
                mURLStat.error(URLStat.ERRTYPE_FORBIDDEN, url);
            }
            return;
        }

        int errType = URLStat.ERRTYPE_NONE;

        try { 

            DataSource source = getDataSource(req, res);
            if (source == null) {
                return;
            }

            res.setContentType(MimeType.SWF);

            String app = LZHttpUtils.getRealPath(mContext, req);
            boolean isClientCacheable = DataSource.isClientCacheable(req);
            if (mCache.isCacheable(req)) {
                if (isClientCacheable) {
                    mLogger.info(
/* (non-Javadoc)
 * @i18n.test
 * @org-mes="proxying " + p[0] + ", cacheable on server and client"
 */
                        org.openlaszlo.i18n.LaszloMessages.getMessage(
                                ResponderCache.class.getName(),"051018-377", new Object[] {url})
);
                } else {
                    mLogger.info(
/* (non-Javadoc)
 * @i18n.test
 * @org-mes="proxying " + p[0] + ", cacheable on server and not client"
 */
                        org.openlaszlo.i18n.LaszloMessages.getMessage(
                                ResponderCache.class.getName(),"051018-386", new Object[] {url})
);
                }
                mCache.getAsSWF(app, req, res, source);
            } else {
                if (isClientCacheable) {
                    mLogger.info(
/* (non-Javadoc)
 * @i18n.test
 * @org-mes="proxying " + p[0] + ", not cacheable on server and cacheable on the client"
 */
                        org.openlaszlo.i18n.LaszloMessages.getMessage(
                                ResponderCache.class.getName(),"051018-398", new Object[] {url})
);
                } else {
                    mLogger.info(
/* (non-Javadoc)
 * @i18n.test
 * @org-mes="proxying " + p[0] + ", not cacheable on server or client"
 */
                        org.openlaszlo.i18n.LaszloMessages.getMessage(
                                ResponderCache.class.getName(),"051018-407", new Object[] {url})
);
                }
                source.getAsSWF(app, req, res, getConverter());
            }
        } catch (ConversionException e) {
            respondWithErrorSWF(res, 
/* (non-Javadoc)
 * @i18n.test
 * @org-mes="data conversion error for " + p[0] + ": " + p[1]
 */
                        org.openlaszlo.i18n.LaszloMessages.getMessage(
                                ResponderCache.class.getName(),"051018-419", new Object[] {url, e.getMessage()})
                                );
            errType = URLStat.ERRTYPE_CONVERSION;
        } catch (DataSourceException e) {
                respondWithErrorSWF(res, 
/* (non-Javadoc)
 * @i18n.test
 * @org-mes="data source error for " + p[0] + ": " + p[1]
 */
                        org.openlaszlo.i18n.LaszloMessages.getMessage(
                                ResponderCache.class.getName(),"051018-428", new Object[] {url, e.getMessage()})
                                );
            errType = URLStat.ERRTYPE_DATA_SOURCE;
        } catch (UnknownHostException e) {
            respondWithErrorSWF(res, 
/* (non-Javadoc)
 * @i18n.test
 * @org-mes="unknown host for " + p[0] + ": " + p[1]
 */
                        org.openlaszlo.i18n.LaszloMessages.getMessage(
                                ResponderCache.class.getName(),"051018-437", new Object[] {url, e.getMessage()})
                                );
            errType = URLStat.ERRTYPE_UNKNOWN_HOST;
        } catch (URIException e) {
            respondWithErrorSWF(res, 
/* (non-Javadoc)
 * @i18n.test
 * @org-mes="bad url: " + p[0]
 */
                        org.openlaszlo.i18n.LaszloMessages.getMessage(
                                ResponderCache.class.getName(),"051018-446", new Object[] {e.getMessage()})
);
            errType = URLStat.ERRTYPE_MALFORMED_URL;
        } catch (MalformedURLException e) {
            respondWithErrorSWF(res, 
/* (non-Javadoc)
 * @i18n.test
 * @org-mes="bad url: " + p[0]
 */
                        org.openlaszlo.i18n.LaszloMessages.getMessage(
                                ResponderCache.class.getName(),"051018-446", new Object[] {e.getMessage()})
);
            errType = URLStat.ERRTYPE_MALFORMED_URL;
        } catch (InterruptedIOException e) {
            respondWithErrorSWF(res, 
/* (non-Javadoc)
 * @i18n.test
 * @org-mes="backend timeout for " + p[0] + ": " + p[1]
 */
                        org.openlaszlo.i18n.LaszloMessages.getMessage(
                                ResponderCache.class.getName(),"051018-466", new Object[] {url, e.getMessage()})
                                );
            errType = URLStat.ERRTYPE_TIMEOUT;
        } catch (IOException e) {
            // Handle SocketTimeoutExceptions as timeouts instead of IO issues
            Class stec = null;
            try {
                stec = Class.forName("java.net.SocketTimeoutException");
            } catch (ClassNotFoundException cfne) {
            }
            if (stec != null && stec.isAssignableFrom(e.getClass())) {
                errType = URLStat.ERRTYPE_TIMEOUT;
                respondWithErrorSWF(res, 
/* (non-Javadoc)
 * @i18n.test
 * @org-mes="backend timeout for " + p[0] + ": " + p[1]
 */
                        org.openlaszlo.i18n.LaszloMessages.getMessage(
                                ResponderCache.class.getName(),"051018-466", new Object[] {url, e.getMessage()})
                                );
            } else {
                respondWithExceptionSWF(res, e);
                errType = URLStat.ERRTYPE_IO;
            }
        } catch (IllegalArgumentException e) {
            respondWithExceptionSWF(res, e);
            errType = URLStat.ERRTYPE_ILLEGAL_ARGUMENT;
        } catch (Throwable e) { 
            // Makes much easier to debug runtime exceptions
            // but perhaps not strictly correct.
            respondWithExceptionSWF(res, e);
            errType = URLStat.ERRTYPE_OTHER;
        } 

        if (mCollectStat) {
            if (errType == URLStat.ERRTYPE_NONE)
                mURLStat.success(url);
            else 
                mURLStat.error(errType, url);
        }
    }

    /**
     * @return the datasource for this request
     */
    protected DataSource getDataSource(HttpServletRequest req, 
                                       HttpServletResponse res) 
        throws MalformedURLException, URIException
    {
        String ds = "http";
        String urlstr = DataSource.getURL(req);
        if (urlstr != null) {
            mLogger.debug("urlstr " + urlstr);
            URI uri = LZHttpUtils.newURI(urlstr);
            String protocol = uri.getScheme();
            if (protocol != null && protocol.equals("https")) {
                    protocol = "http";
            }

            ds = protocol;
        }

        mLogger.debug("ds is " + ds);

        DataSource source = null;
        if (ds == null) {
            source = mHTTPDataSource;
        } else {
            if ("soap".equals(ds)) {
                // SOAP: dispatch on runtime ('lzr' query arg ) to figure out what
                // kind of datatype the client wants (SWF or JSON).
                // lzr == "dhtml" ? JSON : SWF

                String runtime = req.getParameter("lzr");
                if ("dhtml".equals(runtime)) {
                    ds = "soap-json";
                } else {
                    ds = "soap-swf";
                }
            }

            source = (DataSource) mDataSourceMap.get(ds);
            if (source == null) 
                respondWithErrorSWF(res, 
/* (non-Javadoc)
 * @i18n.test
 * @org-mes="Can't find a data source for " + p[0]
 */
                        org.openlaszlo.i18n.LaszloMessages.getMessage(
                                ResponderCache.class.getName(),"051018-540", new Object[] {urlstr})
);
        }
        return source;
    }

    public int getMimeType()
    {
        return MIME_TYPE_SWF;
    }

    public float getFlashPlayerVersion(HttpServletRequest req) {
        float fpv = (float)-1.0;
        try {
            String _fpv = req.getParameter("fpv");
            if (_fpv != null)
                fpv = Float.parseFloat(_fpv);
        } catch (NumberFormatException e) {
            mLogger.debug(e.getMessage());
        }
        return fpv;
    }

    /**
     * @return the converter to be used by this cache
     */
    public Converter getConverter() {
        return mCache.getConverter();
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.