Package org.apache.myfaces.portlet.faces.testsuite.common.util.faces.application

Source Code of org.apache.myfaces.portlet.faces.testsuite.common.util.faces.application.TestSuiteViewHandlerImpl$StringBuilderWriter

/* 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.myfaces.portlet.faces.testsuite.common.util.faces.application;

import java.io.BufferedWriter;
import java.io.IOException;

import java.io.OutputStreamWriter;
import java.io.Writer;

import java.util.Map;

import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.StateManager;
import javax.faces.application.ViewHandler;
import javax.faces.application.ViewHandlerWrapper;

import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

import javax.faces.context.ResponseWriter;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;

import javax.portlet.PortletContext;
import javax.portlet.RenderResponse;
import javax.portlet.faces.Bridge;
import javax.portlet.faces.BridgeUtil;

import org.apache.myfaces.portlet.faces.testsuite.common.Constants;


/**
* View handler implementation for JSF portlet bridge.
*
* The only method we override here is getActionURL().
*
* TODO JSF 1.2 note: JSF 1.2 RI implements ViewHandler.renderView() differently in order to handle
* emitting non-JSF markup that follows the JSF tags after the JSF renders correctly. Unfortunately,
* the RI handles this by introducing several servlet dependencies. Currently, the bridge handles
* this by overriding the renderView() and ignoring (not interleafing) the non-JSF markup - see HACK
* below
*/
public class TestSuiteViewHandlerImpl extends ViewHandlerWrapper
{

  // the ViewHandler to delegate to
  private ViewHandler mDelegate;

  public TestSuiteViewHandlerImpl(ViewHandler handler)
  {
    mDelegate = handler;
  }
 
  protected ViewHandler getWrapped()
  {
    return mDelegate;
  }
 
  @Override
  public UIViewRoot createView(FacesContext ctx, String viewId)
    throws IllegalArgumentException, NullPointerException
  {
    ctx.getExternalContext().getRequestMap().put("org.apache.portlet.faces.tck.viewCreated", Boolean.TRUE);
    return super.createView(ctx, viewId);
  }
 
  @Override
  public UIViewRoot restoreView(FacesContext ctx, String viewId)
    throws FacesException, NullPointerException
  {
    ctx.getExternalContext().getRequestMap().put("org.apache.portlet.faces.tck.viewCreated", Boolean.FALSE);
    return super.restoreView(ctx, viewId);
  }


  @Override
  public void renderView(FacesContext context, UIViewRoot viewToRender) throws IOException,
                                                                       FacesException
  {
    String testName = (String) context.getExternalContext().getRequestMap().get(Constants.TEST_NAME);
    // Do nothing when not running in portlet request or not the specific test
    if (!BridgeUtil.isPortletRequest() || testName == null || testName.indexOf("renderPolicyTest") < 0)
    {
      super.renderView(context, viewToRender);
      return;
    }
    else
    {
      ExternalContext extCtx = context.getExternalContext();
      Map m = extCtx.getRequestMap();
      // Check to see what the render rule is
      PortletContext pCtx = (PortletContext) extCtx.getContext();
      String policyStr = pCtx.getInitParameter(Bridge.RENDER_POLICY);
      Bridge.BridgeRenderPolicy policy = (policyStr != null) ? Bridge.BridgeRenderPolicy.valueOf(policyStr) : null;

      if (policy == null)
      {
        // no policy so we are to do the default
        m.put("javax.portlet.faces.tck.testRenderPolicyPass", "Bridge correctly delegated first as no render policy was set.");
        throw new FacesException("Can't do a portlet render -- but we expect the bridge to catch this and complete the render itself");
      }
      else if (policy == Bridge.BridgeRenderPolicy.DEFAULT)
      {
        // no policy so we are to do the default
        m.put("javax.portlet.faces.tck.testRenderPolicyPass", "Bridge correctly delegated first as the render policy was DEFAULT.");
        throw new FacesException("Can't do a portlet render -- but we expect the bridge to catch this and complete the render itself");
      }
      else if (policy == Bridge.BridgeRenderPolicy.ALWAYS_DELEGATE)
      {
        m.put("javax.portlet.faces.tck.testRenderPolicyPass", "Bridge correctly delegated as the render policy is ALWAYS_DELEGATE.");
        renderSelf(context, viewToRender);
        return;
      }
      else if (policy == Bridge.BridgeRenderPolicy.NEVER_DELEGATE)
      {
        m.put("javax.portlet.faces.tck.testRenderPolicyFail", "Bridge incorrectly delegated when render policy was NEVER_DELEGATE.");
        renderSelf(context, viewToRender);
      }
    }

  }
 
  private void renderSelf(FacesContext context, UIViewRoot viewToRender) throws IOException,
                                                                       FacesException
  {
    ExternalContext extContext = context.getExternalContext();
    RenderResponse renderResponse = (RenderResponse) extContext.getResponse();

    try
    {

      // set request attribute indicating we can deal with content
      // that is supposed to be delayed until after JSF tree is ouput.
      extContext.getRequestMap().put(Bridge.RENDER_CONTENT_AFTER_VIEW, Boolean.TRUE);
      // TODO JSF 1.2 - executePageToBuildView() creates
      // ViewHandlerResponseWrapper
      // to handle error page and text that exists after the <f:view> tag
      // among other things which have lots of servlet dependencies -
      // we're skipping this for now for portlet
     
     
      // Bridge has had to set this attribute so  Faces RI will skip servlet dependent
      // code when mapping from request paths to viewIds -- however we need to remove it
      // as it screws up the dispatch
      extContext.getRequestMap().remove("javax.servlet.include.servlet_path");
      extContext.dispatch(viewToRender.getViewId());

    }
    catch (IOException e)
    {
      throw new FacesException(e);
    }
   
    // set up the ResponseWriter
    RenderKitFactory renderFactory = (RenderKitFactory) FactoryFinder
                                                                     .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
    RenderKit renderKit = renderFactory.getRenderKit(context, viewToRender.getRenderKitId());

    ResponseWriter oldWriter = context.getResponseWriter();
    StringBuilderWriter strWriter = new StringBuilderWriter(context, 4096);
    ResponseWriter newWriter;
    if (null != oldWriter)
    {
      newWriter = oldWriter.cloneWithWriter(strWriter);
    }
    else
    {
      newWriter = renderKit.createResponseWriter(strWriter, null,
                                                 renderResponse.getCharacterEncoding());
    }
    context.setResponseWriter(newWriter);

    newWriter.startDocument();

    doRenderView(context, viewToRender);

    newWriter.endDocument();

    // replace markers in the body content and write it to response.

    ResponseWriter responseWriter;

    // Dispatch may have output to an OutputStream instead of a Writer
    Writer renderResponseWriter = null;
    try {
      renderResponseWriter = renderResponse.getWriter();
    }
    catch (IllegalStateException ise) {    
      // got this exception because we've called getOutputStream() previously
      renderResponseWriter = new BufferedWriter(
                          new OutputStreamWriter(
                               renderResponse.getPortletOutputStream(),
                               renderResponse.getCharacterEncoding()));
    }
    if (null != oldWriter)
    {
      responseWriter = oldWriter.cloneWithWriter(renderResponseWriter);
    }
    else
    {
      responseWriter = newWriter.cloneWithWriter(renderResponseWriter);
    }
    context.setResponseWriter(responseWriter);

    strWriter.write(responseWriter);
    renderResponseWriter.flush();

    if (null != oldWriter)
    {
      context.setResponseWriter(oldWriter);
    }

    Object content = extContext.getRequestMap().get(Bridge.AFTER_VIEW_CONTENT);
    if (content != null)
    {
      if (content instanceof char[])
      {
        renderResponse.getWriter().write(new String((char[]) content));
      }
      else if (content instanceof byte[])
      {
        renderResponse.getWriter().write(new String((byte[]) content));
      }
      else
      {
        throw new IOException("PortletViewHandlerImpl: invalid" + "AFTER_VIEW_CONTENT buffer type");
      }
    }
    renderResponse.flushBuffer();
  }
 
  public String getActionURL(FacesContext context, String viewId)
  {
    // Call super to get the actionURL
    String resultURL = super.getActionURL(context, viewId);
   
    // Then test to see if we are in a render and this is an encodeActionURL test that
    // tests the render encoding -- if so add the appropriate parameters to test.
    if (BridgeUtil.getPortletRequestPhase() == Bridge.PortletPhase.RENDER_PHASE)
    {
      String testName = (String) context.getExternalContext().getRequestMap().get(Constants.TEST_NAME);
      if (testName == null) return resultURL;
      if (testName.equals("encodeActionURLWithParamRenderTest"))
        return appendQueryString(resultURL, "param1=testValue");
      else if (testName.equals("encodeActionURLWithModeRenderTest"))
        return appendQueryString(resultURL, "javax.portlet.faces.PortletMode=edit&param1=testValue");
      else if (testName.equals("encodeActionURLWithInvalidModeRenderTest"))
        return appendQueryString(resultURL, "javax.portlet.faces.PortletMode=blue&param1=testValue");
      else if (testName.equals("encodeActionURLWithWindowStateRenderTest"))
        return appendQueryString(resultURL, "javax.portlet.faces.WindowState=maximized&param1=testValue");
      else if (testName.equals("encodeActionURLWithInvalidWindowStateRenderTest"))
        return appendQueryString(resultURL, "javax.portlet.faces.WindowState=blue&param1=testValue");
      else if (testName.equals("encodeActionURLWithSecurityRenderTest"))
        return appendQueryString(resultURL, "javax.portlet.faces.Secure=true&param1=testValue");
      else if (testName.equals("encodeActionURLWithInvalidSecurityRenderTest"))
        return appendQueryString(resultURL, "javax.portlet.faces.Secure=blue&param1=testValue");
    }
    return resultURL;
  }
 
  private String appendQueryString(String url, String params)
  {
    if (url.indexOf('?') < 0)
      return url + "?" + params;
    else
      return url + "&" + params;
  }
 
  private void doRenderView(FacesContext context, UIViewRoot viewToRender) throws IOException,
                                                                          FacesException
  {
    viewToRender.encodeAll(context);
  }

  private static final class StringBuilderWriter extends Writer
  {
    private StringBuilder       mBuilder;
    private FacesContext        mContext;

    // TODO: These bridge needs to use it's own constants here. This will
    // confine
    // us to only work with the R.I.
    private static final String RI_SAVESTATE_FIELD_MARKER = "~com.sun.faces.saveStateFieldMarker~";
    private static final String MYFACES_SAVESTATE_FIELD_MARKER = "<!--@@JSF_FORM_STATE_MARKER@@-->";
    private static String sSaveStateFieldMarker = null;

    public StringBuilderWriter(FacesContext context, int initialCapacity)
    {
      if (initialCapacity < 0)
      {
        throw new IllegalArgumentException();
      }
      mBuilder = new StringBuilder(initialCapacity);
      mContext = context;
    }

    @Override
    public void write(char[] cbuf, int off, int len) throws IOException
    {
      if (off < 0 || off > cbuf.length || len < 0 || off + len > cbuf.length || off + len < 0)
      {
        throw new IndexOutOfBoundsException();
      }
      else if (len == 0)
      {
        return;
      }
      mBuilder.append(cbuf, off, len);
    }

    @Override
    public void flush() throws IOException
    {
    }

    @Override
    public void close() throws IOException
    {
    }

    /**
     * Write a string.
     *
     * @param str
     *          String to be written
     */
    @Override
    public void write(String str)
    {
      mBuilder.append(str);
    }

    @Override
    public void write(String str, int off, int len)
    {
      write(str.substring(off, off + len));
    }

    public StringBuilder getBuffer()
    {
      return mBuilder;
    }

    @Override
    public String toString()
    {
      return mBuilder.toString();
    }

    public void write(Writer writer) throws IOException
    {

      // See if we already have determined the SAVESTATE_FIELD_MARKER in use
      // If not then determine it and set for future use
      if (sSaveStateFieldMarker == null)
      {
        sSaveStateFieldMarker = determineSaveStateFieldMarker();
      }
     
      // TODO: Buffer?
      int pos = 0;
     
      // First we need to make sure we save the view
      StateManager stateManager = mContext.getApplication().getStateManager();
      Object stateToWrite = stateManager.saveView(mContext);
     
      // If we didn't find a savestate_field_marker don't search to replace for one.
      if (sSaveStateFieldMarker != null)
      {
        int markLen = sSaveStateFieldMarker.length();
        int tildeIdx = mBuilder.indexOf(sSaveStateFieldMarker);
        while (tildeIdx > 0)
        {
          writer.write(mBuilder.substring(pos, tildeIdx));
          stateManager.writeState(mContext, stateToWrite);
          pos = tildeIdx + markLen;
          tildeIdx = mBuilder.indexOf(sSaveStateFieldMarker, pos);
        }
      }
     
      writer.write(mBuilder.substring(pos));
    }
   
    private String determineSaveStateFieldMarker() throws IOException
    {
      // First check to see if there is one set in the configuration - if so test it first
      String marker = ((PortletContext)FacesContext.getCurrentInstance().
                    getExternalContext().getContext()).getInitParameter(Bridge.SAVESTATE_FIELD_MARKER);

      if (isMarker(marker))
      {
        return marker;
      }
      // wasn't that one so test the Faces RI marker
      else if (isMarker(RI_SAVESTATE_FIELD_MARKER))
      {
        return RI_SAVESTATE_FIELD_MARKER;
      }
      // wasn't that one so test the MyFaces marker
      else if (isMarker(MYFACES_SAVESTATE_FIELD_MARKER))
      {
        return MYFACES_SAVESTATE_FIELD_MARKER;
      }
     
      // log that we didn't find a marker
      // However ignore this "exceptional" situation because its not so exceptional
      // MyFaces actually directly writes the state into the response more commonly
      // than it writes the Marker. 
      mContext.getExternalContext().log("Unable to locate a SAVESTATE_FIELD_MARKER in response.  This could be because your Faces environment doesn't write such a marker or because the bridge doesn't know the marker in use.  If the later, configure the appropriate application init parameter javax.portlet.faces.SAVESTATE_FIELD_MARKER.");
      return null;
    }
   
    private boolean isMarker(String marker)
    {
      return marker != null && mBuilder.indexOf(marker) >= 0;
    }
  }
 


}
TOP

Related Classes of org.apache.myfaces.portlet.faces.testsuite.common.util.faces.application.TestSuiteViewHandlerImpl$StringBuilderWriter

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.