protected string getPath(QuercusHttpServletRequest req)
        {
            // php/8173
            string pwd = getQuercus().getPwd().copy();

            string servletPath = QuercusRequestAdapter.getPageServletPath(req);

            if (servletPath.startsWith("/"))
            {
                servletPath = servletPath.substring(1);
            }

            string path = pwd.lookupChild(servletPath);

            // php/2010, php/2011, php/2012
            if (path.isFile())
            {
                return(path);
            }

            StringBuilder sb = new StringBuilder();

            sb.append(servletPath);

            string pathInfo = QuercusRequestAdapter.getPagePathInfo(req);

            if (pathInfo != null)
            {
                sb.append(pathInfo);
            }

            string scriptPath = sb.ToString();

            path = pwd.lookupChild(scriptPath);

            return(path);

            /* jetty getRealPath() de-references symlinks, which causes problems with MergePath
             * // php/8173
             * string pwd = getQuercus().getPwd().copy();
             *
             * string scriptPath = QuercusRequestAdapter.getPageServletPath(req);
             * string pathInfo = QuercusRequestAdapter.getPagePathInfo(req);
             *
             * string path = pwd.lookup(req.getRealPath(scriptPath));
             *
             * if (path.isFile())
             * return path;
             *
             * // XXX: include
             *
             * string fullPath;
             * if (pathInfo != null)
             * fullPath = scriptPath + pathInfo;
             * else
             * fullPath = scriptPath;
             *
             * return pwd.lookup(req.getRealPath(fullPath));
             */
        }
        protected string getPath(QuercusHttpServletRequest req)
        {
            // copy to clear status cache
            // XXX: improve caching so we don't need to do this anymore
            string pwd = _pwd.copy();

            StringBuilder sb          = new StringBuilder();
            string        servletPath = QuercusRequestAdapter.getPageServletPath(req);

            if (servletPath.startsWith("/"))
            {
                sb.append(servletPath, 1, servletPath.length());
            }
            else
            {
                sb.append(servletPath);
            }

            string pathInfo = QuercusRequestAdapter.getPagePathInfo(req);

            if (pathInfo != null)
            {
                sb.append(pathInfo);
            }

            string scriptPath = sb.ToString();

            string path = pwd.lookupChild(scriptPath);

            return(path);
        }
        /**
         * Fills the map.
         */
        private void fillMap()
        {
            if (_isFilled)
            {
                return;
            }

            _isFilled = true;

            for (Map.Entry <Value, Value> entry
                 : _env.getQuercus().getServerEnvMap().entrySet())
            {
                super.put(entry.getKey(), entry.getValue());
            }

            QuercusHttpServletRequest request = _env.getRequest();
            bool isUnicode = _env.isUnicodeSemantics();

            if (request != null)
            {
                super.put(isUnicode ? SERVER_ADDR_VU : SERVER_ADDR_V,
                          _env.createString(request.getLocalAddr()));
                super.put(isUnicode ? SERVER_NAME_VU : SERVER_NAME_V,
                          _env.createString(request.getServerName()));

                super.put(isUnicode ? SERVER_PORT_VU : SERVER_PORT_V,
                          LongValue.create(request.getServerPort()));
                super.put(isUnicode ? REMOTE_HOST_VU : REMOTE_HOST_V,
                          _env.createString(request.getRemoteHost()));
                super.put(isUnicode ? REMOTE_ADDR_VU : REMOTE_ADDR_V,
                          _env.createString(request.getRemoteAddr()));
                super.put(isUnicode ? REMOTE_PORT_VU : REMOTE_PORT_V,
                          LongValue.create(request.getRemotePort()));

                // Drupal's optional activemenu plugin only works on Apache servers!
                // bug at http://drupal.org/node/221867
                super.put(isUnicode ? SERVER_SOFTWARE_VU : SERVER_SOFTWARE_V,
                          _env.createString("Apache PHP Quercus("
                                            + _env.getQuercus().getVersion()
                                            + ")"));

                super.put(isUnicode ? SERVER_PROTOCOL_VU : SERVER_PROTOCOL_V,
                          _env.createString(request.getProtocol()));
                super.put(isUnicode ? REQUEST_METHOD_VU : REQUEST_METHOD_V,
                          _env.createString(request.getMethod()));

                string queryString = QuercusRequestAdapter.getPageQueryString(request);
                string requestURI  = QuercusRequestAdapter.getPageURI(request);
                string servletPath = QuercusRequestAdapter.getPageServletPath(request);
                string pathInfo    = QuercusRequestAdapter.getPagePathInfo(request);
                string contextPath = QuercusRequestAdapter.getPageContextPath(request);

                if (queryString != null)
                {
                    super.put(isUnicode ? QUERY_STRING_VU : QUERY_STRING_V,
                              _env.createString(queryString));
                }

                // XXX: a better way?
                // getRealPath() returns a native path
                // need to convert windows paths to resin paths
                string root = request.getRealPath("/");

                if (root == null)
                {
                    root = _env.getPwd().getFullPath();
                }

                if (root.indexOf('\\') >= 0)
                {
                    root = root.replace('\\', '/');
                    root = '/' + root;
                }

                super.put(isUnicode ? DOCUMENT_ROOT_VU : DOCUMENT_ROOT_V,
                          _env.createString(root));
                super.put(isUnicode ? SCRIPT_NAME_VU : SCRIPT_NAME_V,
                          _env.createString(contextPath + servletPath));
                super.put(isUnicode ? SCRIPT_URL_VU : SCRIPT_URL_V,
                          _env.createString(requestURI));

                if (queryString != null)
                {
                    requestURI = requestURI + '?' + queryString;
                }

                super.put(isUnicode ? REQUEST_URI_VU : REQUEST_URI_V,
                          _env.createString(requestURI));

                super.put(isUnicode ? REQUEST_TIME_VU : REQUEST_TIME_V,
                          LongValue.create(_env.getStartTime() / 1000));

                super.put(isUnicode ? REQUEST_TIME_FLOAT_VU : REQUEST_TIME_FLOAT_V,
                          DoubleValue.create(_env.getMicroTime() / 1000000.0));

                super.put(isUnicode ? SCRIPT_FILENAME_VU : SCRIPT_FILENAME_V,
                          _env.createString(request.getRealPath(servletPath)));

                if (pathInfo != null)
                {
                    super.put(isUnicode ? PATH_INFO_VU : PATH_INFO_V,
                              _env.createString(pathInfo));
                    super.put(isUnicode ? PATH_TRANSLATED_VU : PATH_TRANSLATED_V,
                              _env.createString(request.getRealPath(pathInfo)));
                }

                if (request.isSecure())
                {
                    super.put(isUnicode ? HTTPS_VU : HTTPS_V,
                              _env.createString("on"));

                    // #5402
                    super.put(isUnicode ? HTTP_X_SSL_REQUEST_VU : HTTP_X_SSL_REQUEST_V,
                              _env.createString("on"));
                }

                if (pathInfo == null)
                {
                    super.put(isUnicode ? PHP_SELF_VU : PHP_SELF_V,
                              _env.createString(contextPath + servletPath));
                }
                else
                {
                    super.put(isUnicode ? PHP_SELF_VU : PHP_SELF_V,
                              _env.createString(contextPath + servletPath + pathInfo));
                }

                // authType @is not set on Tomcat
                //String authType = request.getAuthType();
                string authHeader = request.getHeader("Authorization");

                if (authHeader != null)
                {
                    if (authHeader.indexOf("Basic") == 0)
                    {
                        super.put(isUnicode ? AUTH_TYPE_VU : AUTH_TYPE_V,
                                  _env.createString("Basic"));

                        bool userNameIsSet = false;
                        if (request.getRemoteUser() != null)
                        {
                            super.put(isUnicode ? PHP_AUTH_USER_VU : PHP_AUTH_USER_V,
                                      _env.createString(request.getRemoteUser()));
                            userNameIsSet = true;
                        }
                        string digest = authHeader.substring("Basic ".length());

                        string userPass = Base64.decode(digest);

                        int i = userPass.indexOf(':');
                        if (i > 0)
                        {
                            if (!userNameIsSet)
                            {
                                super.put(isUnicode ? PHP_AUTH_USER_VU : PHP_AUTH_USER_V,
                                          _env.createString(userPass.substring(0, i)));
                            }
                            super.put(isUnicode ? PHP_AUTH_PW_VU : PHP_AUTH_PW_V,
                                      _env.createString(userPass.substring(i + 1)));
                        }
                    }
                    else if (authHeader.indexOf("Digest") == 0)
                    {
                        super.put(isUnicode ? AUTH_TYPE_VU : AUTH_TYPE_V,
                                  _env.createString("Digest"));

                        string digest = authHeader.substring("Digest ".length());

                        super.put(isUnicode ? PHP_AUTH_DIGEST_VU : PHP_AUTH_DIGEST_V,
                                  _env.createString(digest));
                    }
                }

                Enumeration e = request.getHeaderNames();
                while (e.hasMoreElements())
                {
                    string key = (String)e.nextElement();

                    string value = request.getHeader(key);

                    if (key.equalsIgnoreCase("Host"))
                    {
                        super.put(isUnicode ? HTTP_HOST_VU : HTTP_HOST_V,
                                  _env.createString(value));
                    }
                    else if (key.equalsIgnoreCase("Content-Length"))
                    {
                        super.put(isUnicode ? CONTENT_LENGTH_VU : CONTENT_LENGTH_V,
                                  _env.createString(value));
                    }
                    else if (key.equalsIgnoreCase("Content-Type"))
                    {
                        super.put(isUnicode ? CONTENT_TYPE_VU : CONTENT_TYPE_V,
                                  _env.createString(value));
                    }
                    else
                    {
                        super.put(convertHttpKey(key), _env.createString(value));
                    }
                }
            }
        }
        public override void service(ServletRequest request, ServletResponse response)

        {
            QuercusHttpServletRequest req
                = new QuercusHttpServletRequestImpl((HttpServletRequest)request);
            HttpServletResponse res = (HttpServletResponse)response;

            string uri = QuercusRequestAdapter.getPageURI(req);

            CacheEntry entry = _cache.get(uri);

            if (entry == null)
            {
                string path    = getPath(req);
                string relPath = path.getUserPath();

                string mimeType = _context.getMimeType(uri);

                entry = new CacheEntry(path, relPath, mimeType);
                _cache.put(uri, entry);
            }
            else if (entry.isModified())
            {
                entry = new CacheEntry(entry.getPath(),
                                       entry.getRelPath(),
                                       entry.getMimeType());

                _cache.put(uri, entry);
            }

            string ifMatch = req.getHeader("If-None-Match");
            string etag    = entry.getEtag();

            if (ifMatch != null && ifMatch.equals(etag))
            {
                res.addHeader("ETag", etag);
                res.sendError(HttpServletResponse.SC_NOT_MODIFIED);
                return;
            }

            string lastModified = entry.getLastModifiedString();

            if (ifMatch == null)
            {
                string ifModified = req.getHeader("If-Modified-Since");

                bool isModified = true;

                if (ifModified == null)
                {
                }
                else if (ifModified.equals(lastModified))
                {
                    isModified = false;
                }
                else
                {
                    long ifModifiedTime;

                    QDate date = QDate.allocateLocalDate();

                    try {
                        ifModifiedTime = date.parseDate(ifModified);
                    } catch (Exception e) {
                        log.log(Level.FINER, e.ToString(), e);

                        ifModifiedTime = 0;
                    }

                    QDate.freeLocalDate(date);

                    isModified = ifModifiedTime == 0 ||
                                 ifModifiedTime != entry.getLastModified();
                }

                if (!isModified)
                {
                    if (etag != null)
                    {
                        res.addHeader("ETag", etag);
                    }

                    res.sendError(HttpServletResponse.SC_NOT_MODIFIED);
                    return;
                }
            }

            res.addHeader("ETag", etag);
            res.addHeader("Last-Modified", lastModified);

            string mime = entry.getMimeType();

            if (mime != null)
            {
                res.setContentType(mime);
            }

            res.setContentLength((int)entry.getLength());

            string method = req.getMethod();

            if (method.equalsIgnoreCase("HEAD"))
            {
                return;
            }

            string path = entry.getPath();

            if (path.isDirectory())
            {
                res.sendError(HttpServletResponse.SC_NOT_FOUND);

                return;
            }
            else if (!path.canRead())
            {
                res.sendError(HttpServletResponse.SC_NOT_FOUND);

                return;
            }

            OutputStream os = response.getOutputStream();

            path.writeToStream(os);
        }