org.apache.cocoon.servletservice.ServletServiceContext.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.cocoon.servletservice.ServletServiceContext.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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.apache.cocoon.servletservice;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.cocoon.servletservice.util.ServletContextWrapper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @version $Id: ServletServiceContext.java 637585 2008-03-16 13:14:12Z gkossakowski $
 * @since 1.0.0
 */
public class ServletServiceContext extends ServletContextWrapper implements Absolutizable {

    public static final String SUPER = "super";

    private final Log logger = LogFactory.getLog(ServletServiceContext.class);

    private Map attributes = new Hashtable();
    private Servlet servlet;
    private String mountPath;
    private String contextPath;
    private Map properties;
    private Map connections;
    private Map connectionServiceNames;

    private String serviceName;

    /*
     *  TODO inheritance of attributes from the parent context is only
     *  partly implemented: removeAttribute and getAttributeNames
     *  doesn't respect inheritance yet.
     */
    public Object getAttribute(String name) {
        Object value = this.attributes.get(name);
        return value != null ? value : super.getAttribute(name);
    }

    public void setAttribute(String name, Object value) {
        this.attributes.put(name, value);
    }

    public void removeAttribute(String name) {
        this.attributes.remove(name);
    }

    public Enumeration getAttributeNames() {
        return Collections.enumeration(this.attributes.keySet());
    }

    /**
     * @param map the attributes to set
     */
    public void setAttributes(Map map) {
        if (map != null)
            this.attributes = map;
    }

    public URL getResource(String path) throws MalformedURLException {
        // HACK: allow file:/ URLs for reloading of sitemaps during development
        if (this.contextPath.startsWith("file:")) {
            return new URL("file", null, this.contextPath.substring("file:".length()) + path);
        }

        // prefix the path with the servlet context resolve and resolve in the embedding
        // servlet context
        return super.getResource(this.contextPath + path);
    }

    public String getRealPath(String path) {
        // We better don't assume that blocks are unpacked
        return null;
    }

    // FIXME, this should be defined in the config instead
    public String getInitParameter(String name) {
        if (this.properties == null) {
            return null;
        }

        String value = (String) this.properties.get(name);
        // Ask the super servlet for the property
        if (value == null) {
            ServletContext superContext = this.getNamedContext(SUPER);
            if (superContext != null) {
                value = superContext.getInitParameter(name);
            }
        }

        // Ask the parent context
        if (value == null) {
            value = super.getInitParameter(name);
        }

        return value;
    }

    public Enumeration getInitParameterNames() {
        Vector names = new Vector();

        // add all names of the parent servlet context
        Enumeration enumeration = super.getInitParameterNames();
        while (enumeration.hasMoreElements()) {
            names.add(enumeration.nextElement());
        }

        // add names of the super servlet
        ServletContext superContext = this.getNamedContext(SUPER);
        if (superContext != null) {
            enumeration = superContext.getInitParameterNames();
            while (enumeration.hasMoreElements()) {
                names.add(enumeration.nextElement());
            }
        }

        // add property names of this servlet
        if (this.properties != null) {
            names.addAll(this.properties.keySet());
        }

        return names.elements();
    }

    public InputStream getResourceAsStream(String path) {
        try {
            return this.getResource(path).openStream();
        } catch (IOException e) {
            // FIXME Error handling
            e.printStackTrace();
            return null;
        }
    }

    public ServletContext getContext(String uripath) {
        return null;
    }

    public int getMajorVersion() {
        return 2;
    }

    /*
     * (non-Javadoc)
     *
     * @see javax.servlet.ServletContext#getMinorVersion()
     */
    public int getMinorVersion() {
        return 3;
    }

    private Collection getDirectoryList(File file, String pathPrefix) {
        ArrayList filenames = new ArrayList();

        if (!file.isDirectory()) {
            filenames.add("/" + file.toString().substring(pathPrefix.length() - 1));
            return filenames;
        }

        File[] files = file.listFiles();

        for (int i = 0; i < files.length; i++) {
            File subfile = files[i];
            filenames.addAll(getDirectoryList(subfile, pathPrefix));
        }

        return filenames;
    }

    public Set getResourcePaths(String path) {
        if (path == null) {
            return Collections.EMPTY_SET;
        }

        String pathPrefix;
        if (this.contextPath.startsWith("file:")) {
            pathPrefix = this.contextPath.substring("file:".length());
        } else {
            pathPrefix = this.contextPath;
        }

        path = pathPrefix + path;

        File file = new File(path);

        if (!file.exists()) {
            return Collections.EMPTY_SET;
        }

        HashSet set = new HashSet();
        set.addAll(getDirectoryList(file, pathPrefix));

        return set;
    }

    public RequestDispatcher getRequestDispatcher(String path) {
        PathDispatcher dispatcher = new PathDispatcher(path);
        return dispatcher.exists() ? dispatcher : null;
    }

    public RequestDispatcher getNamedDispatcher(String name) {
        NamedDispatcher dispatcher = new NamedDispatcher(name);
        return dispatcher.exists() ? dispatcher : null;
    }

    public String getServerInfo() {
        // TODO Auto-generated method stub
        return null;
    }

    public String getServletContextName() {
        // TODO Auto-generated method stub
        return null;
    }

    // Servlet service specific methods

    /**
     * Set the servlet of the context
     * @param servlet
     */
    public void setServlet(Servlet servlet) {
        this.servlet = servlet;
    }

    /**
     * Takes the scheme specific part of a servlet service URI (the scheme is the
     * responsibilty of the ServletSource) and resolve it with respect to the
     * servlets mount point.
     */
    public URI absolutizeURI(URI uri) throws URISyntaxException {
        String servletServiceName = uri.getScheme();
        ServletServiceContext servletServiceContext;
        if (servletServiceName == null) {
            // this servlet service
            servletServiceContext = this;
        } else {
            // another servlet service
            servletServiceContext = (ServletServiceContext) this.getNamedContext(servletServiceName);
            if (servletServiceContext == null) {
                throw new URISyntaxException(uri.toString(), "Unknown servlet service name");
            }
        }

        String mountPath = servletServiceContext.getMountPath();
        if (mountPath == null) {
            throw new URISyntaxException(uri.toString(), "No mount point for this URI");
        }
        if (mountPath.endsWith("/")) {
            mountPath = mountPath.substring(0, mountPath.length() - 1);
        }

        String absoluteURI = mountPath + uri.getSchemeSpecificPart();
        if (logger.isInfoEnabled()) {
            logger.info("Resolving " + uri.toString() + " to " + absoluteURI);
        }

        return new URI(absoluteURI);
    }

    public String getServiceName(String connectionName) {
        return (String) this.connectionServiceNames.get(connectionName);
    }

    public String getServiceName() {
        return this.serviceName;
    }

    /**
     * Get the context of a servlet service with a given name.
     */
    // FIXME implement NPE handling
    public ServletContext getNamedContext(String name) {
        if (this.connections == null) {
            return null;
        }

        Servlet servlet = (Servlet) this.connections.get(name);
        if (servlet == null && !name.equals(SUPER)) {
            Servlet _super = ((Servlet) this.connections.get(SUPER));
            if (_super != null) {
                ServletContext c = _super.getServletConfig().getServletContext();
                if (c instanceof ServletServiceContext)
                    return ((ServletServiceContext) c).getNamedContext(name);

                return null;
            }
        }
        return servlet != null ? servlet.getServletConfig().getServletContext() : null;
    }

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

    /**
     * Get the mount path of the servlet service context
     */
    public String getMountPath() {
        return this.mountPath;
    }

    /**
     * @param contextPath
     */
    public void setContextPath(String contextPath) {
        this.contextPath = contextPath;
    }

    /**
     * @param properties The properties to set.
     */
    public void setInitParams(Map properties) {
        this.properties = properties;
    }

    /**
     * @param connections the connections to set
     */
    public void setConnections(Map connections) {
        this.connections = connections;
    }

    /**
     * @param connections the service names of the connections
     */
    public void setConnectionServiceNames(Map connectionServletServiceNames) {
        this.connectionServiceNames = connectionServletServiceNames;
    }

    /**
     * @param serviceName the name of the
     */
    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    protected class NamedDispatcher implements RequestDispatcher {

        private String servletServiceName;

        private boolean superCall;

        private ServletContext context;

        public NamedDispatcher(String servletServiceName) {
            this.servletServiceName = servletServiceName;
            this.superCall = SUPER.equals(this.servletServiceName);

            // Call to a named servlet service that exists in the current
            // context
            this.context = ServletServiceContext.this.getNamedContext(this.servletServiceName);
        }

        protected boolean exists() {
            return this.context != null;
        }

        public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
            // Call to named servlet service

            if (logger.isInfoEnabled()) {
                logger.info("Enter processing in servlet service " + this.servletServiceName);
            }
            RequestDispatcher dispatcher = this.context
                    .getRequestDispatcher(((HttpServletRequest) request).getPathInfo());
            if (dispatcher != null && dispatcher instanceof PathDispatcher) {
                ((PathDispatcher) dispatcher).forward(request, response, this.superCall);
            } else {
                // Cannot happen
                throw new IllegalStateException();
            }
            if (logger.isInfoEnabled()) {
                logger.info("Leaving processing in servlet service " + this.servletServiceName);
            }
        }

        public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException {
            throw new UnsupportedOperationException();
        }
    }

    /**
     * Limited functionality, assumes that there is at most one servlet in the
     * context
     */
    private class PathDispatcher implements RequestDispatcher {

        // Ignores path, as the assumed only servlet within the context is
        // implicitly mounted on '/*'
        private PathDispatcher(String path) {
        }

        private boolean exists() {
            return ServletServiceContext.this.servlet != null;
        }

        public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
            forward(request, response, false);
        }

        protected void forward(ServletRequest request, ServletResponse response, boolean superCall)
                throws ServletException, IOException {
            try {
                HttpServletResponseBufferingWrapper wrappedResponse = new HttpServletResponseBufferingWrapper(
                        (HttpServletResponse) response);
                // FIXME: I think that Cocoon should always set status code on
                // its own
                wrappedResponse.setStatus(HttpServletResponse.SC_OK);
                if (!superCall) {
                    // It is important to set the current context each time
                    // a new context is entered, this is used for the servlet
                    // protocol
                    CallStackHelper.enterServlet(ServletServiceContext.this, (HttpServletRequest) request,
                            wrappedResponse);
                } else {
                    // A super servlet service should be called in the context
                    // of the called servlet service to get polymorphic calls
                    // resolved in the right way. We still need to register the
                    // current context for resolving super calls relative it.
                    CallStackHelper.enterSuperServlet(ServletServiceContext.this, (HttpServletRequest) request,
                            wrappedResponse);
                }

                ServletServiceContext.this.servlet.service(request, wrappedResponse);

                int status = wrappedResponse.getStatusCode();
                NamedDispatcher _super = (NamedDispatcher) ServletServiceContext.this.getNamedDispatcher(SUPER);
                if (status == HttpServletResponse.SC_NOT_FOUND && _super != null) {
                    //if servlet returned NOT_FOUND (404) and has super servlet declared let's reset everything and ask the super servlet

                    //wrapping object resets underlying response as well 
                    wrappedResponse.resetBufferedResponse();
                    //here we don't need to pass wrappedResponse object because it's not our concern to buffer response anymore
                    //this avoids many overlapping buffers
                    _super.forward(request, response);
                } else {
                    wrappedResponse.flushBufferedResponse();
                }
            } finally {
                CallStackHelper.leaveServlet();
            }
        }

        public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException {
            throw new UnsupportedOperationException();
        }
    }

}