Package javax.portlet.faces

Source Code of javax.portlet.faces.GenericFacesPortlet

/*
* 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 javax.portlet.faces;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletConfig;
import javax.portlet.PortletContext;
import javax.portlet.PortletException;
import javax.portlet.PortletMode;
import javax.portlet.PortletRequest;
import javax.portlet.PortletRequestDispatcher;
import javax.portlet.PortletResponse;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.WindowState;

/**
* The <code>GenericFacesPortlet</code> is provided to simplify development of a portlet that in
* whole or part relies on the Faces bridge to process requests. If all requests are to be handled
* by the bridge, <code>GenericFacesPortlet</code> is a turnkey implementation. Developers do not
* need to subclass it. However, if there are some situations where the portlet doesn't require
* bridge services then <code>GenericFacesPortlet</code> can be subclassed and overriden.
* <p>
* Since <code>GenericFacesPortlet</code> subclasses <code>GenericPortlet</code> care is taken
* to all subclasses to override naturally. For example, though <code>doDispatch()</code> is
* overriden, requests are only dispatched to the bridge from here if the <code>PortletMode</code>
* isn't <code>VIEW</code>, <code>EDIT</code>, or <code>HELP</code>.
* <p>
* The <code>GenericFacesPortlet</code> recognizes the following portlet initialization
* parameters:
* <ul>
* <li><code>javax.portlet.faces.defaultViewId.[<i>mode</i>]</code>: specifies on a per mode
* basis the default viewId the Bridge executes when not already encoded in the incoming request. A
* value must be defined for each <code>PortletMode</code> the <code>Bridge</code> is expected
* to process. </li>
<li><code>javax.portlet.faces.excludedRequestAttributes</code>: specifies on a per portlet
* basis the set of request attributes the bridge is to exclude from its request scope.  The
* value of this parameter is a comma delimited list of either fully qualified attribute names or
* a partial attribute name of the form <i>packageName.*</i>.  In this later case all attributes
* exactly prefixed by <i>packageName</i> are excluded, non recursive.</li>
<li><code>javax.portlet.faces.preserveActionParams</code>: specifies on a per portlet
* basis whether the bridge should preserve parameters received in an action request
* and restore them for use during subsequent renders.</li>
<li><code>javax.portlet.faces.defaultContentType</code>: specifies on a per mode
* basis the content type the bridge should set for all render requests it processes. </li>
<li><code>javax.portlet.faces.defaultCharacterSetEncoding</code>: specifies on a per mode
* basis the default character set encoding the bridge should set for all render requests it
* processes</li>
* </ul>
* The <code>GenericFacesPortlet</code> recognizes the following application
* (<code>PortletContext</code>) initialization parameters:
* <ul>
* <li><code>javax.portlet.faces.BridgeImplClass</code>: specifies the <code>Bridge</code>implementation
* class used by this portlet. Typically this initialization parameter isn't set as the
* <code>GenericFacesPortlet</code> defaults to finding the class name from the bridge
* configuration.  However if more then one bridge is configured in the environment such
* per application configuration is necessary to force a specific bridge to be used.
* </li>
* </ul>
*/
public class GenericFacesPortlet extends GenericPortlet
{
  /** Application (PortletContext) init parameter that names the bridge class used
   * by this application.  Typically not used unless more then 1 bridge is configured
   * in an environment as its more usual to rely on the self detection.
   */
  public static final String BRIDGE_CLASS = Bridge.BRIDGE_PACKAGE_PREFIX + "BridgeImplClass";

  /** Portlet init parameter that defines the default ViewId that should be used
   * when the request doesn't otherwise convery the target.  There must be one
   * initialization parameter for each supported mode.  Each parameter is named
   * DEFAULT_VIEWID.<i>mode</i>, where <i>mode</i> is the name of the corresponding
   * <code>PortletMode</code>
   */
  public static final String DEFAULT_VIEWID = Bridge.BRIDGE_PACKAGE_PREFIX + "defaultViewId";

  /** Portlet init parameter that defines the render response ContentType the bridge
   * sets prior to rendering.  If not set the bridge uses the request's preferred
   * content type.
   */
  public static final String DEFAULT_CONTENT_TYPE =
    Bridge.BRIDGE_PACKAGE_PREFIX + "defaultContentType";

  /** Portlet init parameter that defines the render response CharacterSetEncoding the bridge
   * sets prior to rendering.  Typcially only set when the jsp outputs an encoding other
   * then the portlet container's and the portlet container supports response encoding
   * transformation.
   */
  public static final String DEFAULT_CHARACTERSET_ENCODING =
    Bridge.BRIDGE_PACKAGE_PREFIX + "defaultCharacterSetEncoding";

  /** Location of the services descriptor file in a brige installation that defines
   * the class name of the bridge implementation.
   */
  public static final String BRIDGE_SERVICE_CLASSPATH =
    "META-INF/services/javax.portlet.faces.Bridge";

  private Class<? extends Bridge> mFacesBridgeClass = null;
  private Bridge mFacesBridge = null;
  private HashMap<String, String> mDefaultViewIdMap = null;
  private Object mLock = new Object()// used to synchronize on when initializing the bridge.

  /**
   * Initialize generic faces portlet from portlet.xml
   */
  @SuppressWarnings("unchecked")
  @Override
  public void init(PortletConfig portletConfig) throws PortletException
  {
    super.init(portletConfig);

    // Make sure the bridge impl class is defined -- if not then search for it
    // using same search rules as Faces
    String bridgeClassName = getBridgeClassName();

    if (bridgeClassName != null)
    {
      try
      {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        mFacesBridgeClass = (Class<? extends Bridge>) loader.loadClass(bridgeClassName);
      } catch (ClassNotFoundException cnfe)
      {
        throw new PortletException("Unable to load configured bridge class: " + bridgeClassName);
      }
    }
    else
    {
      throw new PortletException("Can't locate configuration parameter defining the bridge class to use for this portlet:" + getPortletName());
    }

    // Get the other bridge configuration parameters and set as context attributes
    List<String> excludedAttrs = getExcludedRequestAttributes();
    if (excludedAttrs != null)
    {
      getPortletContext().setAttribute(Bridge.BRIDGE_PACKAGE_PREFIX + getPortletName() + "." +
                                       Bridge.EXCLUDED_REQUEST_ATTRIBUTES, excludedAttrs);
    }

    Boolean preserveActionParams = new Boolean(isPreserveActionParameters());
    getPortletContext().setAttribute(Bridge.BRIDGE_PACKAGE_PREFIX + getPortletName() + "." +
                                     Bridge.PRESERVE_ACTION_PARAMS, preserveActionParams);

    Map defaultViewIdMap = getDefaultViewIdMap();
    getPortletContext().setAttribute(Bridge.BRIDGE_PACKAGE_PREFIX + getPortletName() + "." +
                                     Bridge.DEFAULT_VIEWID_MAP, defaultViewIdMap);

    // Don't instanciate/initialize the bridge yet. Do it on first use
  }

  /**
   * Release resources, specifically it destroys the bridge.
   */
  @Override
  public void destroy()
  {
    if (mFacesBridge != null)
    {
      mFacesBridge.destroy();
      mFacesBridge = null;
      mFacesBridgeClass = null;
    }
    mDefaultViewIdMap = null;
   
    super.destroy();
  }

  /**
   * If mode is VIEW, EDIT, or HELP -- defer to the doView, doEdit, doHelp so subclasses can
   * override. Otherwise handle mode here if there is a defaultViewId mapping for it.
   */
  @Override
  public void doDispatch(RenderRequest request, RenderResponse response) throws PortletException,
                                                                                IOException
  {
    // Defer to helper methods for standard modes so subclasses can override
    PortletMode mode = request.getPortletMode();
    if (mode == PortletMode.EDIT || mode == PortletMode.HELP || mode == PortletMode.VIEW)
    {
      super.doDispatch(request, response);
    } else
    {
      // Bridge didn't process this one -- so forge ahead
      if (!doRenderDispatchInternal(request, response))
      {
        super.doDispatch(request, response);
      }
    }
  }

  @Override
  protected void doEdit(RenderRequest request, RenderResponse response) throws PortletException,
                                                                               java.io.IOException
  {
    doRenderDispatchInternal(request, response);
  }

  @Override
  protected void doHelp(RenderRequest request, RenderResponse response) throws PortletException,
                                                                               java.io.IOException
  {
    doRenderDispatchInternal(request, response);
  }

  @Override
  protected void doView(RenderRequest request, RenderResponse response) throws PortletException,
                                                                               java.io.IOException
  {
    doRenderDispatchInternal(request, response);
  }

  @Override
  public void processAction(ActionRequest request,
                            ActionResponse response) throws PortletException, IOException
  {
    doActionDispatchInternal(request, response);
  }

  /**
   * Returns the set of RequestAttribute names that the portlet wants the bridge to
   * exclude from its managed request scope.  This default implementation picks up
   * this list from the comma delimited init_param javax.portlet.faces.excludedRequestAttributes.
   *
   * @return a List containing the names of the attributes to be excluded. null if it can't be
   *         determined.
   */
  public List<String> getExcludedRequestAttributes()
  {
    String excludedAttrs =
      getPortletConfig().getInitParameter(Bridge.BRIDGE_PACKAGE_PREFIX + Bridge.EXCLUDED_REQUEST_ATTRIBUTES);
    if (excludedAttrs == null)
    {
      return null;
    }

    String[] attrArray = excludedAttrs.split(",");
    // process comma delimited String into a List
    ArrayList<String> list = new ArrayList(attrArray.length);
    for (int i = 0; i < attrArray.length; i++)
    {
      list.add(attrArray[i]);
    }
    return list;
  }

  /**
   * Returns a boolean indicating whether or not the bridge should preserve all the
   * action parameters in the subsequent renders that occur in the same scope.  This
   * default implementation reads the values from the portlet init_param
   * javax.portlet.faces.preserveActionParams.  If not present, false is returned.
   *
   * @return a boolean indicating whether or not the bridge should preserve all the
   * action parameters in the subsequent renders that occur in the same scope.
   */
  public boolean isPreserveActionParameters()
  {
    String preserveActionParams =
      getPortletConfig().getInitParameter(Bridge.BRIDGE_PACKAGE_PREFIX +
                                          Bridge.PRESERVE_ACTION_PARAMS);
    if (preserveActionParams == null)
    {
      return false;
    } else
    {
      return Boolean.parseBoolean(preserveActionParams);
    }
  }

  /**
   * Returns the className of the bridge implementation this portlet uses. Subclasses override to
   * alter the default behavior. Default implementation first checks for a portlet context init
   * parameter: javax.portlet.faces.BridgeImplClass. If it doesn't exist then it looks for the
   * resource file "META-INF/services/javax.portlet.faces.Bridge" using the current threads
   * classloader and extracts the classname from the first line in that file.
   *
   * @return the class name of the Bridge class the GenericFacesPortlet uses. null if it can't be
   *         determined.
   */
  public String getBridgeClassName()
  {
    String bridgeClassName = getPortletConfig().getPortletContext().getInitParameter(BRIDGE_CLASS);

    if (bridgeClassName == null)
    {
      bridgeClassName =
          getFromServicesPath(getPortletConfig().getPortletContext(), BRIDGE_SERVICE_CLASSPATH);
    }
    return bridgeClassName;
  }

  /**
   * Returns the default content type for this portlet request. Subclasses override to
   * alter the default behavior. Default implementation returns value of the portlet context init
   * parameter: javax.portlet.faces.DefaultContentType. If it doesn't exist the portlet
   * request's preferred response content type is returned.
   *
   * Note:  This support is specific to the Portlet 1.0 Bridge.  Its value is
   * likely to be ignored by the Portlet 2.0 Bridge or later.
   *
   * @return the content type that should be used for this response.
   */
  public String getResponseContentType(PortletRequest request)
  {
    String contentType =
      getPortletConfig().getPortletContext().getInitParameter(DEFAULT_CONTENT_TYPE);

    if (contentType == null || !isInRequestedContentTypes(request, contentType))
    {
      contentType = request.getResponseContentType();
    }
    return contentType;
  }
 
  private boolean isInRequestedContentTypes(PortletRequest request, String contentTypeToCheck)
  {
    Enumeration e = request.getResponseContentTypes();
    while (e.hasMoreElements())
    {
      if (contentTypeToCheck.equalsIgnoreCase((String) e.nextElement()))
      {
        return true;
      }
    }
    return false;
  }

  /**
   * Returns the character set encoding used for this portlet response. Subclasses override to
   * alter the default behavior. Default implementation returns value of the portlet context init
   * parameter: javax.portlet.faces.DefaultCharacterSetEncoding. If it doesn't exist null
   * is returned.
   *
   * Note:  This support is specific to the Portlet 1.0 Bridge.  Its value is
   * likely to be ignored by the Portlet 2.0 Bridge or later.
   *
   * @return the content type that should be used for this response.
   */
  public String getResponseCharacterSetEncoding(PortletRequest request)
  {
    return getPortletConfig().getPortletContext().getInitParameter(DEFAULT_CHARACTERSET_ENCODING);
  }


  /**
   * Returns the defaultViewIdMap the bridge should use when its unable to resolve to a specific
   * target in the incoming request. There is one entry per support <code>PortletMode
   * </code>.  The entry key is the name of the mode.  The entry value is the default viewId
   * for that mode.
   *
   * @return the defaultViewIdMap
   */
  public Map getDefaultViewIdMap()
  {
    if (mDefaultViewIdMap == null)
    {
      mDefaultViewIdMap = new HashMap<String, String>();
      // loop through all portlet initialization parameters looking for those in the
      // correct form
      PortletConfig config = getPortletConfig();

      Enumeration<String> e = config.getInitParameterNames();
      int len = DEFAULT_VIEWID.length();
      while (e.hasMoreElements())
      {
        String s = e.nextElement();
        if (s.startsWith(DEFAULT_VIEWID) && s.length() > DEFAULT_VIEWID.length())
        {
          String viewId = config.getInitParameter(s);
          // extract the mode
          s = s.substring(len + 1);
          mDefaultViewIdMap.put(s, viewId);
        }
      }
    }

    return mDefaultViewIdMap;
  }
 
  /**
   * Returns an initialized bridge instance adequately prepared so the caller can
   * call doFacesRequest directly without further initialization.
   *
   * @return instance of the bridge.
   * @throws PortletException exception acquiring or initializting the bridge.
   */
  public Bridge getFacesBridge(PortletRequest request,
                               PortletResponse response) throws PortletException
  {
    initBridgeRequest(request, response);
    return mFacesBridge;
 
 
  private boolean isNonFacesRequest(PortletRequest request, PortletResponse response)
  {
    // Non Faces request is identified by either the presence of the _jsfBridgeNonFacesView
    // parameter or the request being for a portlet mode which doesn't have a default
    // Faces view configured for it.
    if (request.getParameter(Bridge.NONFACES_TARGET_PATH_PARAMETER) != null)
    {
      return true;
    }

    String modeDefaultViewId = mDefaultViewIdMap.get(request.getPortletMode().toString());
    return modeDefaultViewId == null;
  }

  private void doActionDispatchInternal(ActionRequest request,
                                        ActionResponse response) throws PortletException,
                                                                        IOException
  {
    // First determine whether this is a Faces or nonFaces request
    if (isNonFacesRequest(request, response))
    {
      throw new PortletException("GenericFacesPortlet:  Action request is not for a Faces target.  Such nonFaces requests must be handled by a subclass.");
    } else
    {
      doBridgeDispatch(request, response);
    }
  }

  private boolean doRenderDispatchInternal(RenderRequest request,
                                           RenderResponse response) throws PortletException,
                                                                           IOException
  {
    // First determine whether this is a Faces or nonFaces request
    if (isNonFacesRequest(request, response))
    {
      return doNonFacesDispatch(request, response);
    } else
    {
      WindowState state = request.getWindowState();
      if (!state.equals(WindowState.MINIMIZED))
      {
        doBridgeDispatch(request, response);
      }
      return true;
    }
  }

  private boolean doNonFacesDispatch(RenderRequest request,
                                     RenderResponse response) throws PortletException
  {
    // Can only dispatch if the path is encoded in the request parameter
    String targetPath = request.getParameter(Bridge.NONFACES_TARGET_PATH_PARAMETER);
    if (targetPath == null)
    {
      // Didn't handle this request
      return false;
    }

    // merely dispatch this to the nonJSF target
    // but because this is portlet 1.0 we have to ensure the content type is set.
    // Ensure the ContentType is set before rendering
    if (response.getContentType() == null)
    {
      response.setContentType(request.getResponseContentType());
    }
    try
    {
      PortletRequestDispatcher dispatcher =
        this.getPortletContext().getRequestDispatcher(targetPath);
      dispatcher.include(request, response);
      return true;
    } catch (Exception e)
    {
      throw new PortletException("Unable to dispatch to: " + targetPath, e);
    }
  }

  private void doBridgeDispatch(RenderRequest request,
                                RenderResponse response) throws PortletException
  {
    // Set the response ContentType/CharacterSet
    setResponseContentType(response, getResponseContentType(request),
                           getResponseCharacterSetEncoding(request));

    try
    {
      getFacesBridge(request, response).doFacesRequest(request, response);
    } catch (BridgeException e)
    {
      throw new PortletException("doBridgeDispatch failed:  error from Bridge in executing the request",
                                 e);
    }

  }

  private void doBridgeDispatch(ActionRequest request,
                                ActionResponse response) throws PortletException
  {

    try
    {
      getFacesBridge(request, response).doFacesRequest(request, response);
    } catch (BridgeException e)
    {
      throw new PortletException("doBridgeDispatch failed:  error from Bridge in executing the request",
                                 e);
    }

  }

  private void initBridgeRequest(PortletRequest request,
                                 PortletResponse response) throws PortletException
  {
    initBridge();


    // Now do any per request initialization
    // I nthis case look to see if the request is encoded (usually
    // from a NonFaces view response) with the specific Faces
    // view to execute.
    String view = request.getParameter(Bridge.FACES_VIEW_ID_PARAMETER);
    if (view != null)
    {
      request.setAttribute(Bridge.VIEW_ID, view);
    } else
    {
      view = request.getParameter(Bridge.FACES_VIEW_PATH_PARAMETER);
      if (view != null)
      {
        request.setAttribute(Bridge.VIEW_PATH, view);
      }
    }
  }

  private void initBridge() throws PortletException
  {
    // Ensure te Bridge has been constrcuted and initialized
    if (mFacesBridge == null)
    {
      try
      {
        // ensure we only ever create/init one bridge per portlet
        synchronized(mLock)
        {
          if (mFacesBridge == null)
          {
            mFacesBridge = mFacesBridgeClass.newInstance();
            mFacesBridge.init(getPortletConfig());
          }
        }
      }
      catch (Exception e)
      {
        throw new PortletException("doBridgeDisptach:  error instantiating the bridge class", e);
      }
    }
  }

  private void setResponseContentType(RenderResponse response, String contentType,
                                      String charSetEncoding)
  {
    if (contentType == null)
    {
      return;

    }
    if (charSetEncoding != null)
    {
      StringBuffer buf = new StringBuffer(contentType);
      buf.append(";");
      buf.append(charSetEncoding);
      response.setContentType(buf.toString());
    } else
    {
      response.setContentType(contentType);
    }
  }

  private String getFromServicesPath(PortletContext context, String resourceName)
  {
    // Check for a services definition
    String result = null;
    BufferedReader reader = null;
    InputStream stream = null;
    try
    {
      ClassLoader cl = Thread.currentThread().getContextClassLoader();
      if (cl == null)
      {
        return null;
      }

      stream = cl.getResourceAsStream(resourceName);
      if (stream != null)
      {
        // Deal with systems whose native encoding is possibly
        // different from the way that the services entry was created
        try
        {
          reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
        } catch (UnsupportedEncodingException e)
        {
          reader = new BufferedReader(new InputStreamReader(stream));
        }
        result = reader.readLine();
        if (result != null)
        {
          result = result.trim();
        }
        reader.close();
        reader = null;
        stream = null;
      }
    } catch (IOException e)
    {
    } catch (SecurityException e)
    {
    } finally
    {
      if (reader != null)
      {
        try
        {
          reader.close();
          stream = null;
        } catch (Throwable t)
        {
          ;
        }
        reader = null;
      }
      if (stream != null)
      {
        try
        {
          stream.close();
        } catch (Throwable t)
        {
          ;
        }
        stream = null;
      }
    }
    return result;
  }

}
TOP

Related Classes of javax.portlet.faces.GenericFacesPortlet

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.