zweb/src/main/java/org/zkoss/web/portlet/Portlets.java

Summary

Maintainability
B
5 hrs
Test Coverage
/* Portlets.java

    Purpose:
        
    Description:
        
    History:
        Fri Apr  1 13:57:54     2005, Created by tomyeh

Copyright (C) 2005 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
    This program is distributed under LGPL Version 2.1 in the hope that
    it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.web.portlet;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;

import javax.portlet.PortletContext;
import javax.portlet.PortletException;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.zkoss.lang.SystemException;
import org.zkoss.web.servlet.Servlets;
import org.zkoss.web.util.resource.ExtendletContext;

/**
 * Utilities to handle portlet.
 *
 * @author tomyeh
 */
public class Portlets {
    private static final Logger log = LoggerFactory.getLogger(Portlets.class);

    /**
     * Includes the resource at the specified URI.
     * It enhances RequestDispatcher to allow the inclusion with
     * a parameter map -- actually converting parameters to a query string
     * and appending it to uri.
     *
     * <p>NOTE: don't include query parameters in uri.
     *
     * @param uri the URI to include. It is OK to relevant (without leading
     * '/'). If starts with "/", the context path of request is assumed.
     * To reference to foreign context, use "~ctx/" where ctx is the
     * context path of the foreign context (without leading '/').
     * @param params the parameter map; null to ignore
     * @param mode one of {@link #OVERWRITE_URI}, {@link #IGNORE_PARAM},
     * and {@link #APPEND_PARAM}. It defines how to handle if both uri
     * and params contains the same parameter.
     */
    public static final void include(PortletContext ctx, RenderRequest request, RenderResponse response, String uri,
            Map params, int mode) throws IOException, PortletException {
        final PortletRequestDispatcher disp = getRequestDispatcher(ctx, uri, params, mode);
        if (disp == null)
            throw new PortletException("No dispatcher available to include " + uri);
        disp.include(request, response);
    }

    /** Returns the request dispatch of the specified URI.
     *
     * @param ctx the context used to resolve a foreign context.
     * It is required only if uri starts with "~".
     * @param uri the URI to include. It is OK to relevant (without leading
     * '/'). If starts with "/", the context path of request is assumed.
     * To reference to foreign context, use "~ctx/" where ctx is the
     * context path of the foreign context (without leading '/').
     * @param params the parameter map; null to ignore
     * @param mode one of {@link #OVERWRITE_URI}, {@link #IGNORE_PARAM},
     * and {@link #APPEND_PARAM}. It defines how to handle if both uri
     * and params contains the same parameter.
     */
    public static final PortletRequestDispatcher getRequestDispatcher(PortletContext ctx, String uri, Map params,
            int mode) throws PortletException {
        return new ParsedURI(ctx, uri).getRequestDispatcher(params, mode);
    }

    /** Returns the resource of the specified uri.
     * Unlike PortletContext.getResource, it handles "~" like
     * {@link #getRequestDispatcher} did.
     */
    public static final URL getResource(PortletContext ctx, String uri) throws MalformedURLException {
        return new ParsedURI(ctx, uri).getResource();
    }

    /** Returns the resource stream of the specified uri.
     * Unlike PortletContext.getResource, it handles "~" like
     * {@link #getRequestDispatcher} did.
     */
    public static final InputStream getResourceAsStream(PortletContext ctx, String uri) {
        return new ParsedURI(ctx, uri).getResourceAsStream();
    }

    /** Used to resolve "~" in URI. */
    private static class ParsedURI {
        /** The portlet context, null if _svlctx or _extctx is need. */
        private PortletContext _prtctx;
        /** External context if ~xxx/ is specified. */
        private ServletContext _svlctx;
        /** The extended context. */
        private ExtendletContext _extctx;
        private String _uri;

        private ParsedURI(final PortletContext ctx, final String uri) {
            if (uri != null && uri.length() > 0 && uri.charAt(0) == '~') { //refer to foreign context
                final int j = uri.indexOf('/', 1);
                final String ctxroot;
                if (j >= 0) {
                    ctxroot = "/" + uri.substring(1, j);
                    _uri = uri.substring(j);
                } else {
                    ctxroot = "/" + uri.substring(1);
                    _uri = "/";
                }

                final ServletContext svlctx = getServletContext(ctx);
                _extctx = Servlets.getExtendletContext(svlctx, ctxroot.substring(1));
                if (_extctx == null) {
                    _svlctx = svlctx;
                    _svlctx = _svlctx.getContext(ctxroot);
                    if (_svlctx == null)
                        throw new SystemException("Context not found or not visible to " + ctx + ": " + ctxroot);
                }
            } else {
                _prtctx = ctx;
                _uri = uri;
            }
        }

        private PortletRequestDispatcher getRequestDispatcher(Map params, int mode) {
            if (_extctx == null && _svlctx == null && _prtctx == null) //not found
                return null;

            final String uri = generateURI(_uri, params, mode);
            if (_prtctx != null)
                return _prtctx.getRequestDispatcher(uri);

            final RequestDispatcher rd = _svlctx != null ? _svlctx.getRequestDispatcher(uri)
                    : _extctx.getRequestDispatcher(uri);
            return ServletPortletDispatcher.getInstance(rd);
        }

        private URL getResource() throws MalformedURLException {
            return _prtctx != null ? _prtctx.getResource(_uri)
                    : _svlctx != null ? _svlctx.getResource(_uri) : _extctx != null ? _extctx.getResource(_uri) : null;
        }

        private InputStream getResourceAsStream() {
            return _prtctx != null ? _prtctx.getResourceAsStream(_uri)
                    : _svlctx != null ? _svlctx.getResourceAsStream(_uri)
                            : _extctx != null ? _extctx.getResourceAsStream(_uri) : null;
        }
    }

    private static final ServletContext getServletContext(PortletContext ctx) {
        return PortletServletContext.getInstance(ctx);
    }

    /** Whether to overwrite uri if both uri and params contain the same
     * parameter.
     * Used by {@link #generateURI}
     */
    public static final int OVERWRITE_URI = Servlets.OVERWRITE_URI;
    /** Whether to ignore params if both uri and params contain the same
     * parameter.
     * Used by {@link #generateURI}
     */
    public static final int IGNORE_PARAM = Servlets.IGNORE_PARAM;
    /** Whether to append params if both uri and params contain the same
     * parameter. In other words, they both appear as the final query string.
     * Used by {@link #generateURI}
     */
    public static final int APPEND_PARAM = Servlets.APPEND_PARAM;

    /** Generates URI by appending the parameters.
     * @param params the parameters to append to the query string
     * @param mode one of {@link #OVERWRITE_URI}, {@link #IGNORE_PARAM},
     * and {@link #APPEND_PARAM}. It defines how to handle if both uri
     * and params contains the same parameter.
     * mode is used only if both uri contains query string and params is
     * not empty.
     */
    public static final String generateURI(String uri, Map params, int mode) {
        return Servlets.generateURI(uri, params, mode);
    }
}