001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
003     * agreements. See the NOTICE file distributed with this work for additional information regarding
004     * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
005     * "License"); you may not use this file except in compliance with the License. You may obtain a
006     * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable
007     * law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
008     * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
009     * for the specific language governing permissions and limitations under the License.
010     */
011    package javax.portlet.faces;
012    
013    import java.io.BufferedReader;
014    import java.io.IOException;
015    import java.io.InputStream;
016    import java.io.InputStreamReader;
017    import java.io.UnsupportedEncodingException;
018    
019    import java.util.ArrayList;
020    import java.util.Enumeration;
021    import java.util.HashMap;
022    import java.util.List;
023    import java.util.Map;
024    
025    import javax.portlet.ActionRequest;
026    import javax.portlet.ActionResponse;
027    import javax.portlet.EventRequest;
028    import javax.portlet.EventResponse;
029    import javax.portlet.GenericPortlet;
030    import javax.portlet.PortletConfig;
031    import javax.portlet.PortletContext;
032    import javax.portlet.PortletException;
033    import javax.portlet.PortletMode;
034    import javax.portlet.PortletRequest;
035    import javax.portlet.PortletRequestDispatcher;
036    import javax.portlet.PortletResponse;
037    import javax.portlet.RenderRequest;
038    import javax.portlet.RenderResponse;
039    import javax.portlet.ResourceRequest;
040    import javax.portlet.ResourceResponse;
041    import javax.portlet.WindowState;
042    
043    /**
044     * The <code>GenericFacesPortlet</code> is provided to simplify development of a portlet that in
045     * whole or part relies on the Faces bridge to process requests. If all requests are to be handled
046     * by the bridge, <code>GenericFacesPortlet</code> is a turnkey implementation. Developers do not
047     * need to subclass it. However, if there are some situations where the portlet doesn't require
048     * bridge services then <code>GenericFacesPortlet</code> can be subclassed and overriden.
049     * <p>
050     * Since <code>GenericFacesPortlet</code> subclasses <code>GenericPortlet</code> care is taken
051     * to all subclasses to override naturally. For example, though <code>doDispatch()</code> is
052     * overriden, requests are only dispatched to the bridge from here if the <code>PortletMode</code>
053     * isn't <code>VIEW</code>, <code>EDIT</code>, or <code>HELP</code>.
054     * <p>
055     * The <code>GenericFacesPortlet</code> recognizes the following portlet initialization
056     * parameters:
057     * <ul>
058     * <li><code>javax.portlet.faces.defaultViewId.[<i>mode</i>]</code>: specifies on a per mode
059     * basis the default viewId the Bridge executes when not already encoded in the incoming request. A
060     * value must be defined for each <code>PortletMode</code> the <code>Bridge</code> is expected
061     * to process. </li>
062     *  <li><code>javax.portlet.faces.excludedRequestAttributes</code>: specifies on a per portlet
063     * basis the set of request attributes the bridge is to exclude from its request scope.  The
064     * value of this parameter is a comma delimited list of either fully qualified attribute names or
065     * a partial attribute name of the form <i>packageName.*</i>.  In this later case all attributes
066     * exactly prefixed by <i>packageName</i> are excluded, non recursive.</li>
067     *  <li><code>javax.portlet.faces.preserveActionParams</code>: specifies on a per portlet
068     * basis whether the bridge should preserve parameters received in an action request
069     * and restore them for use during subsequent renders.</li>
070     *  <li><code>javax.portlet.faces.defaultContentType</code>: specifies on a per mode
071     * basis the content type the bridge should set for all render requests it processes. </li>
072     *  <li><code>javax.portlet.faces.defaultCharacterSetEncoding</code>: specifies on a per mode
073     * basis the default character set encoding the bridge should set for all render requests it
074     * processes</li>
075     * </ul>
076     * The <code>GenericFacesPortlet</code> recognizes the following application
077     * (<code>PortletContext</code>) initialization parameters:
078     * <ul>
079     * <li><code>javax.portlet.faces.BridgeImplClass</code>: specifies the <code>Bridge</code>implementation
080     * class used by this portlet. Typically this initialization parameter isn't set as the 
081     * <code>GenericFacesPortlet</code> defaults to finding the class name from the bridge
082     * configuration.  However if more then one bridge is configured in the environment such 
083     * per application configuration is necessary to force a specific bridge to be used.
084     * </li>
085     * </ul>
086     */
087    public class GenericFacesPortlet extends GenericPortlet
088    {
089      /** Application (PortletContext) init parameter that names the bridge class used
090       * by this application.  Typically not used unless more then 1 bridge is configured
091       * in an environment as its more usual to rely on the self detection.
092       */
093      public static final String BRIDGE_CLASS = Bridge.BRIDGE_PACKAGE_PREFIX + "BridgeClassName";
094      
095      
096      /** Portlet init parameter that defines the default ViewId that should be used
097       * when the request doesn't otherwise convery the target.  There must be one 
098       * initialization parameter for each supported mode.  Each parameter is named
099       * DEFAULT_VIEWID.<i>mode</i>, where <i>mode</i> is the name of the corresponding
100       * <code>PortletMode</code>
101       */
102      public static final String DEFAULT_VIEWID = Bridge.BRIDGE_PACKAGE_PREFIX + "defaultViewId";
103    
104      /** Portlet init parameter that defines the class which implements 
105       * <code>javax.portlet.faces.BridgeWriteBehindResponse</code> and is
106       * (to be) used by the bridge when using dispatch() to render JSF views.
107       */
108      public static final String BRIDGE_WRITE_BEHIND_RESPONSE_CLASS = Bridge.BRIDGE_PACKAGE_PREFIX + "bridgeWriteBehindResponseClassName";
109      
110    
111      /** Portlet init parameter that defines the render response ContentType the bridge 
112       * sets prior to rendering.  If not set the bridge uses the request's preferred
113       * content type.
114       */
115      public static final String DEFAULT_CONTENT_TYPE = 
116        Bridge.BRIDGE_PACKAGE_PREFIX + "defaultContentType";
117    
118      /** Portlet init parameter that defines the render response CharacterSetEncoding the bridge 
119       * sets prior to rendering.  Typcially only set when the jsp outputs an encoding other
120       * then the portlet container's and the portlet container supports response encoding
121       * transformation.
122       */
123      public static final String DEFAULT_CHARACTERSET_ENCODING = 
124        Bridge.BRIDGE_PACKAGE_PREFIX + "defaultCharacterSetEncoding";
125      
126        /** Portlet init parameter containing the setting for whether the <code>GenericFacesPortlet</code>
127         * overrides event processing by dispatching all events to the bridge or delegates
128         * all event processing to the <code>GenericPortlet</code>.  Default is <code>true</code>.
129         */
130      public static final String BRIDGE_AUTO_DISPATCH_EVENTS = Bridge.BRIDGE_PACKAGE_PREFIX + "autoDispatchEvents";
131    
132      /** Location of the services descriptor file in a brige installation that defines 
133       * the class name of the bridge implementation.
134       */
135      public static final String BRIDGE_SERVICE_CLASSPATH = 
136        "META-INF/services/javax.portlet.faces.Bridge";
137    
138      private Class<? extends Bridge> mFacesBridgeClass = null;
139      private Bridge mFacesBridge = null;
140      private HashMap<String, String> mDefaultViewIdMap = null;
141      private Object mLock = new Object();  // used to synchronize on when initializing the bridge.
142    
143      /**
144       * Initialize generic faces portlet from portlet.xml
145       */
146      @SuppressWarnings("unchecked")
147      @Override
148      public void init(PortletConfig portletConfig) throws PortletException
149      {
150        super.init(portletConfig);
151    
152        // Make sure the bridge impl class is defined -- if not then search for it
153        // using same search rules as Faces
154        String bridgeClassName = getBridgeClassName();
155    
156        if (bridgeClassName != null)
157        {
158          try
159          {
160            ClassLoader loader = Thread.currentThread().getContextClassLoader();
161            mFacesBridgeClass = (Class<? extends Bridge>) loader.loadClass(bridgeClassName);
162          } catch (ClassNotFoundException cnfe)
163          {
164            throw new PortletException("Unable to load configured bridge class: " + bridgeClassName);
165          }
166        }
167        else
168        {
169          throw new PortletException("Can't locate configuration parameter defining the bridge class to use for this portlet:" + getPortletName());
170        }
171    
172        // Get the other bridge configuration parameters and set as context attributes
173        List<String> excludedAttrs = getExcludedRequestAttributes();
174        if (excludedAttrs != null)
175        {
176          getPortletContext().setAttribute(Bridge.BRIDGE_PACKAGE_PREFIX + getPortletName() + "." + 
177                                           Bridge.EXCLUDED_REQUEST_ATTRIBUTES, excludedAttrs);
178        }
179    
180        Boolean preserveActionParams = new Boolean(isPreserveActionParameters());
181        getPortletContext().setAttribute(Bridge.BRIDGE_PACKAGE_PREFIX + getPortletName() + "." + 
182                                         Bridge.PRESERVE_ACTION_PARAMS, preserveActionParams);
183    
184        Map defaultViewIdMap = getDefaultViewIdMap();
185        getPortletContext().setAttribute(Bridge.BRIDGE_PACKAGE_PREFIX + getPortletName() + "." + 
186                                         Bridge.DEFAULT_VIEWID_MAP, defaultViewIdMap);
187        
188        Class<? extends BridgeWriteBehindResponse> writeBehindResponse = getWriteBehindResponse();
189        if (writeBehindResponse != null)
190        getPortletContext().setAttribute(Bridge.BRIDGE_PACKAGE_PREFIX + getPortletName() + "." + 
191                                         Bridge.WRITE_BEHIND_RESPONSE, writeBehindResponse);
192    
193        BridgeEventHandler eventHandler = getBridgeEventHandler();
194        if (eventHandler != null)
195        {
196          getPortletContext().setAttribute(Bridge.BRIDGE_PACKAGE_PREFIX + getPortletName() + "." + 
197                                           Bridge.BRIDGE_EVENT_HANDLER, eventHandler);
198        }
199        
200        BridgePublicRenderParameterHandler prpHandler = getBridgePublicRenderParameterHandler();
201        if (prpHandler != null)
202        {
203          getPortletContext().setAttribute(Bridge.BRIDGE_PACKAGE_PREFIX + getPortletName() + "." + 
204                                           Bridge.BRIDGE_PUBLIC_RENDER_PARAMETER_HANDLER, prpHandler);
205        }
206    
207        // Don't instanciate/initialize the bridge yet. Do it on first use
208      }
209    
210      /**
211       * Release resources, specifically it destroys the bridge.
212       */
213      @Override
214      public void destroy()
215      {
216        if (mFacesBridge != null)
217        {
218          mFacesBridge.destroy();
219          mFacesBridge = null;
220          mFacesBridgeClass = null;
221        }
222        mDefaultViewIdMap = null;
223        
224        super.destroy();
225      }
226    
227      /**
228       * If mode is VIEW, EDIT, or HELP -- defer to the doView, doEdit, doHelp so subclasses can
229       * override. Otherwise handle mode here if there is a defaultViewId mapping for it.
230       */
231      @Override
232      public void doDispatch(RenderRequest request, RenderResponse response) throws PortletException, 
233                                                                                    IOException
234      {
235        // Defer to helper methods for standard modes so subclasses can override
236        PortletMode mode = request.getPortletMode();
237        if (mode.equals(PortletMode.EDIT) || mode.equals(PortletMode.HELP) || mode.equals(PortletMode.VIEW))
238        {
239          super.doDispatch(request, response);
240        } else
241        {
242          // Bridge didn't process this one -- so forge ahead
243          if (!doRenderDispatchInternal(request, response))
244          {
245            super.doDispatch(request, response);
246          }
247        }
248      }
249    
250      @Override
251      protected void doEdit(RenderRequest request, RenderResponse response) throws PortletException, 
252                                                                                   java.io.IOException
253      {
254        doRenderDispatchInternal(request, response);
255      }
256    
257      @Override
258      protected void doHelp(RenderRequest request, RenderResponse response) throws PortletException, 
259                                                                                   java.io.IOException
260      {
261        doRenderDispatchInternal(request, response);
262      }
263    
264      @Override
265      protected void doView(RenderRequest request, RenderResponse response) throws PortletException, 
266                                                                                   java.io.IOException
267      {
268        doRenderDispatchInternal(request, response);
269      }
270    
271      @Override
272      public void processAction(ActionRequest request, 
273                                ActionResponse response) throws PortletException, IOException
274      {
275        doActionDispatchInternal(request, response);
276      }
277    
278      /**
279       * Handles resource requests and dispatches to the Bridge
280       */
281      @Override
282      public void serveResource(ResourceRequest request, 
283                                ResourceResponse response) throws PortletException, IOException
284      {
285        doBridgeDispatch(request, response);
286    
287      }
288    
289      /**
290       * Returns an instance of a BridgeEventHandler used to process portlet events
291       * in a JSF environment.
292       * This default implementation looks for a portlet initParameter that
293       * names the class used to instantiate the handler.
294       * @return an instance of BridgeEventHandler or null if there is none.
295       */
296      public BridgeEventHandler getBridgeEventHandler()
297      {
298        String eventHandlerClass = 
299          getPortletConfig().getInitParameter(Bridge.BRIDGE_PACKAGE_PREFIX + Bridge.BRIDGE_EVENT_HANDLER);
300        if (eventHandlerClass != null)
301        {
302          try
303          {
304            ClassLoader loader = Thread.currentThread().getContextClassLoader();
305            Class<? extends BridgeEventHandler> c = 
306              (Class<? extends BridgeEventHandler>) loader.loadClass(eventHandlerClass);
307            return c.newInstance();
308          } catch (ClassNotFoundException cnfe)
309          {
310            // Do nothing and fall through to null check
311            // TODO: log something
312          } catch (InstantiationException ie)
313          {
314            // Do nothing and fall through to null check
315            // TODO: log something
316          } catch (Exception e)
317          {
318            // Do nothing and fall through to null check
319            // TODO: log something
320          }
321        }
322    
323        return null;
324      }
325      
326      /**
327       * Returns an instance of a BridgePublicRenderParameterHandler used to post
328       * process public render parameter changes that the bridge
329       * has pushed into mapped models.
330       * This default implementation looks for a portlet initParameter that
331       * names the class used to instantiate the handler.
332       * @return an instance of BridgeRenderParameterHandler or null if there is none.
333       */
334      public BridgePublicRenderParameterHandler getBridgePublicRenderParameterHandler()
335      {
336        String prpHandlerClass = 
337          getPortletConfig().getInitParameter(Bridge.BRIDGE_PACKAGE_PREFIX + Bridge.BRIDGE_PUBLIC_RENDER_PARAMETER_HANDLER);
338        if (prpHandlerClass != null)
339        {
340          try
341          {
342            ClassLoader loader = Thread.currentThread().getContextClassLoader();
343            Class<? extends BridgePublicRenderParameterHandler> c = 
344              (Class<? extends BridgePublicRenderParameterHandler>) loader.loadClass(prpHandlerClass);
345            return c.newInstance();
346          } catch (ClassNotFoundException cnfe)
347          {
348            // Do nothing and fall through to null check
349            // TODO: log something
350          } catch (InstantiationException ie)
351          {
352            // Do nothing and fall through to null check
353            // TODO: log something
354          } catch (Exception e)
355          {
356            // Do nothing and fall through to null check
357            // TODO: log something
358          }
359        }
360    
361        return null;
362      }
363    
364    
365    
366      /**
367       * Returns the set of RequestAttribute names that the portlet wants the bridge to
368       * exclude from its managed request scope.  This default implementation picks up
369       * this list from the comma delimited init_param javax.portlet.faces.excludedRequestAttributes.
370       * 
371       * @return a List containing the names of the attributes to be excluded. null if it can't be
372       *         determined.
373       */
374      public List<String> getExcludedRequestAttributes()
375      {
376        String excludedAttrs = 
377          getPortletConfig().getInitParameter(Bridge.BRIDGE_PACKAGE_PREFIX + Bridge.EXCLUDED_REQUEST_ATTRIBUTES);
378        if (excludedAttrs == null)
379        {
380          return null;
381        }
382    
383        String[] attrArray = excludedAttrs.split(",");
384        // process comma delimited String into a List
385        ArrayList<String> list = new ArrayList(attrArray.length);
386        for (int i = 0; i < attrArray.length; i++)
387        {
388          list.add(attrArray[i].trim());
389        }
390        return list;
391      }
392    
393      /**
394       * Returns a boolean indicating whether or not the bridge should preserve all the
395       * action parameters in the subsequent renders that occur in the same scope.  This
396       * default implementation reads the values from the portlet init_param
397       * javax.portlet.faces.preserveActionParams.  If not present, false is returned.
398       * 
399       * @return a boolean indicating whether or not the bridge should preserve all the
400       * action parameters in the subsequent renders that occur in the same scope.
401       */
402      public boolean isPreserveActionParameters()
403      {
404        String preserveActionParams = 
405          getPortletConfig().getInitParameter(Bridge.BRIDGE_PACKAGE_PREFIX + 
406                                              Bridge.PRESERVE_ACTION_PARAMS);
407        if (preserveActionParams == null)
408        {
409          return false;
410        } else
411        {
412          return Boolean.parseBoolean(preserveActionParams);
413        }
414      }
415    
416      /**
417       * Returns the className of the bridge implementation this portlet uses. Subclasses override to
418       * alter the default behavior. Default implementation first checks for a portlet context init
419       * parameter: javax.portlet.faces.BridgeImplClass. If it doesn't exist then it looks for the
420       * resource file "META-INF/services/javax.portlet.faces.Bridge" using the current threads
421       * classloader and extracts the classname from the first line in that file.
422       * 
423       * @return the class name of the Bridge class the GenericFacesPortlet uses. null if it can't be
424       *         determined.
425       */
426      public String getBridgeClassName()
427      {
428        String bridgeClassName = getPortletConfig().getPortletContext().getInitParameter(BRIDGE_CLASS);
429    
430        if (bridgeClassName == null)
431        {
432          bridgeClassName = 
433              getFromServicesPath(getPortletConfig().getPortletContext(), BRIDGE_SERVICE_CLASSPATH);
434        }
435        return bridgeClassName;
436      }
437    
438      /**
439       * Returns the default content type for this portlet request. Subclasses override to
440       * alter the default behavior. Default implementation returns value of the portlet init
441       * parameter: javax.portlet.faces.DefaultContentType. If it doesn't exist the portlet
442       * request's preferred response content type is returned.
443       * 
444       * Note:  This support is specific to the Portlet 1.0 Bridge.  Its value is
445       * likely to be ignored by the Portlet 2.0 Bridge or later.
446       * 
447       * @return the content type that should be used for this response.
448       */
449      public String getResponseContentType(PortletRequest request)
450      {
451        String contentType = 
452          getPortletConfig().getInitParameter(DEFAULT_CONTENT_TYPE);
453    
454        if (contentType == null || !isInRequestedContentTypes(request, contentType))
455        {
456          contentType = request.getResponseContentType();
457        }
458        return contentType;
459      }
460      
461      private boolean isInRequestedContentTypes(PortletRequest request, String contentTypeToCheck)
462      {
463        Enumeration e = request.getResponseContentTypes();
464        while (e.hasMoreElements()) 
465        {
466          if (contentTypeToCheck.equalsIgnoreCase((String) e.nextElement()))
467          {
468            return true;
469          }
470        }
471        return false;
472      }
473    
474      /**
475       * Returns the character set encoding used for this portlet response. Subclasses override to
476       * alter the default behavior. Default implementation returns value of the portlet init
477       * parameter: javax.portlet.faces.DefaultCharacterSetEncoding. If it doesn't exist null
478       * is returned.
479       * 
480       * Note:  This support is specific to the Portlet 1.0 Bridge.  Its value is
481       * likely to be ignored by the Portlet 2.0 Bridge or later.
482       * 
483       * @return the content type that should be used for this response.
484       */
485      public String getResponseCharacterSetEncoding(PortletRequest request)
486      {
487        return getPortletConfig().getInitParameter(DEFAULT_CHARACTERSET_ENCODING);
488      }
489    
490    
491      /**
492       * Returns the defaultViewIdMap the bridge should use when its unable to resolve to a specific
493       * target in the incoming request. There is one entry per support <code>PortletMode
494       * </code>.  The entry key is the name of the mode.  The entry value is the default viewId
495       * for that mode.
496       * 
497       * @return the defaultViewIdMap
498       */
499      public Map getDefaultViewIdMap()
500      {
501        if (mDefaultViewIdMap == null)
502        {
503          mDefaultViewIdMap = new HashMap<String, String>();
504          // loop through all portlet initialization parameters looking for those in the
505          // correct form
506          PortletConfig config = getPortletConfig();
507    
508          Enumeration<String> e = config.getInitParameterNames();
509          int len = DEFAULT_VIEWID.length();
510          while (e.hasMoreElements())
511          {
512            String s = e.nextElement();
513            if (s.startsWith(DEFAULT_VIEWID) && s.length() > DEFAULT_VIEWID.length())
514            {
515              String viewId = config.getInitParameter(s);
516              
517              // Don't add if there isn't a view
518              if (viewId == null || viewId.length() == 0) continue;
519              
520              // extract the mode
521              s = s.substring(len + 1);
522              mDefaultViewIdMap.put(s, viewId);
523            }
524          }
525        }
526    
527        return mDefaultViewIdMap;
528      }
529      
530      /**
531       * Returns an instantiated class that implements the <code>java.portlet.faces.BridgeWriteBehindResponse>/code>
532       * interface the bridge is supposed to use when dispatching render requests.  This is provided
533       * when a portlet wants the JSP writeBehindBehavior and chooses to get this Faces implementation
534       * dependent behavior by using a Portlet 2.0 response wrapper.<br>
535       * Subclasses override to
536       * alter the default behavior. Default implementation returns value of the portlet init
537       * parameter: javax.portlet.faces.BridgeWriteBehindResponseClassName. 
538       * 
539       * @return a instantiated class that implements <code>java.portlet.faces.BridgeWriteBehindResponse>/code>
540       */
541    
542      public Class<? extends BridgeWriteBehindResponse> getWriteBehindResponse() throws PortletException
543      {
544        Class<? extends BridgeWriteBehindResponse> writeBehindResponse = null;
545        String bridgeWriteBehindResponseClassName = 
546          getPortletConfig().getInitParameter(BRIDGE_WRITE_BEHIND_RESPONSE_CLASS);
547    
548        if (bridgeWriteBehindResponseClassName != null)
549        {
550          try
551          {
552            ClassLoader loader = Thread.currentThread().getContextClassLoader();
553            writeBehindResponse = (Class<? extends BridgeWriteBehindResponse>) loader.loadClass(bridgeWriteBehindResponseClassName);
554          }
555          catch (ClassNotFoundException cnfe)
556          {
557            throw new PortletException("Unable to load configured bridge writeBehindResponse: " + bridgeWriteBehindResponseClassName);
558          }
559          catch (ClassCastException cce)
560          {
561            this.getPortletContext().log("GenericFacesPortlet: WARNING - configured BridgeWriteBehindResponse isn't an instance of javax.portlet.faces.BridgeWriteBehindResponse.");
562            // continue on
563          }
564        
565        }
566    
567        return writeBehindResponse;
568      }
569      
570      /**
571       * Returns the value of the portlet initialization parameter
572       * <code>javax.portlet.faces.autoDispatchEvents</code> if non-null or
573       * <code>true</code>, otherwise.
574       * 
575       * @return boolean indicating whether to auto-dispatch all events to the bridge 
576       * or not.
577       */
578      public boolean isAutoDispatchEvents()
579      {
580        String configParam = 
581          getPortletConfig().getPortletContext().getInitParameter(BRIDGE_AUTO_DISPATCH_EVENTS);
582    
583        if (configParam != null)
584        {
585          return Boolean.parseBoolean(configParam);
586        }
587        else
588        {
589          return true;
590        }
591      }
592      
593      /**
594       * Returns an initialized bridge instance adequately prepared so the caller can
595       * call doFacesRequest directly without further initialization.
596       * 
597       * @return instance of the bridge.
598       * @throws PortletException exception acquiring or initializting the bridge.
599       */
600      public Bridge getFacesBridge(PortletRequest request, 
601                                   PortletResponse response) throws PortletException
602      {
603        initBridgeRequest(request, response);
604        return mFacesBridge;
605      }  
606      
607      public void processEvent(EventRequest request, EventResponse response)
608        throws PortletException, java.io.IOException
609      {
610        if (isAutoDispatchEvents())
611        {
612          try
613          {
614            getFacesBridge(request, response).doFacesRequest(request, response);
615          } catch (BridgeException e)
616          {
617            throw new PortletException("doBridgeDispatch failed:  error from Bridge in executing the request", 
618                                       e);
619          }
620        }
621        else
622        {
623          super.processEvent(request, response);
624        }
625      }
626    
627      private boolean isNonFacesRequest(PortletRequest request, PortletResponse response)
628      {
629        // Non Faces request is identified by either the presence of the _jsfBridgeNonFacesView
630        // parameter or the request being for a portlet mode which doesn't have a default
631        // Faces view configured for it.
632        if (request.getParameter(Bridge.NONFACES_TARGET_PATH_PARAMETER) != null)
633        {
634          return true;
635        }
636    
637        String modeDefaultViewId = mDefaultViewIdMap.get(request.getPortletMode().toString());
638        return modeDefaultViewId == null;
639      }
640    
641      private void doActionDispatchInternal(ActionRequest request, 
642                                            ActionResponse response) throws PortletException, 
643                                                                            IOException
644      {
645        // First determine whether this is a Faces or nonFaces request
646        if (isNonFacesRequest(request, response))
647        {
648          throw new PortletException("GenericFacesPortlet:  Action request is not for a Faces target.  Such nonFaces requests must be handled by a subclass.");
649        } else
650        {
651          doBridgeDispatch(request, response);
652        }
653      }
654    
655      private boolean doRenderDispatchInternal(RenderRequest request, 
656                                               RenderResponse response) throws PortletException, 
657                                                                               IOException
658      {
659        // First determine whether this is a Faces or nonFaces request
660        if (isNonFacesRequest(request, response))
661        {
662          return doNonFacesDispatch(request, response);
663        } else
664        {
665          WindowState state = request.getWindowState();
666          if (!state.equals(WindowState.MINIMIZED))
667          {
668            doBridgeDispatch(request, response);
669          }
670          return true;
671        }
672      }
673    
674      private boolean doNonFacesDispatch(RenderRequest request, 
675                                         RenderResponse response) throws PortletException
676      {
677        // Can only dispatch if the path is encoded in the request parameter
678        String targetPath = request.getParameter(Bridge.NONFACES_TARGET_PATH_PARAMETER);
679        if (targetPath == null)
680        {
681          // Didn't handle this request
682          return false;
683        }
684    
685        try
686        {
687          PortletRequestDispatcher dispatcher = 
688            this.getPortletContext().getRequestDispatcher(targetPath);
689          dispatcher.forward(request, response);
690          return true;
691        } catch (Exception e)
692        {
693          throw new PortletException("Unable to dispatch to: " + targetPath, e);
694        }
695      }
696    
697      private void doBridgeDispatch(RenderRequest request, 
698                                    RenderResponse response) throws PortletException
699      {
700        try
701        {
702          getFacesBridge(request, response).doFacesRequest(request, response);
703        } catch (BridgeException e)
704        {
705          throw new PortletException("doBridgeDispatch failed:  error from Bridge in executing the request", 
706                                     e);
707        }
708    
709      }
710    
711      private void doBridgeDispatch(ActionRequest request, 
712                                    ActionResponse response) throws PortletException
713      {
714        try
715        {
716          getFacesBridge(request, response).doFacesRequest(request, response);
717        } catch (BridgeException e)
718        {
719          throw new PortletException("doBridgeDispatch failed:  error from Bridge in executing the request", 
720                                     e);
721        }
722    
723      }
724    
725      private void doBridgeDispatch(ResourceRequest request, 
726                                    ResourceResponse response) throws PortletException
727      {
728        try
729        {
730          getFacesBridge(request, response).doFacesRequest(request, response);
731        } catch (BridgeException e)
732        {
733          throw new PortletException("doBridgeDispatch failed:  error from Bridge in executing the request", 
734                                     e);
735        }
736    
737      }
738    
739      private void initBridgeRequest(PortletRequest request, 
740                                     PortletResponse response) throws PortletException
741      {
742        initBridge();
743    
744    
745        // Now do any per request initialization
746        // In this case look to see if the request is encoded (usually 
747        // from a NonFaces view response) with the specific Faces
748        // view to execute.
749        String view = request.getParameter(Bridge.FACES_VIEW_ID_PARAMETER);
750        if (view != null)
751        {
752          request.setAttribute(Bridge.VIEW_ID, view);
753        } else
754        {
755          view = request.getParameter(Bridge.FACES_VIEW_PATH_PARAMETER);
756          if (view != null)
757          {
758            request.setAttribute(Bridge.VIEW_PATH, view);
759          }
760        }
761      }
762    
763      private void initBridge() throws PortletException
764      {
765        // Ensure te Bridge has been constrcuted and initialized
766        if (mFacesBridge == null)
767        {
768          try
769          {
770            // ensure we only ever create/init one bridge per portlet
771            synchronized(mLock)
772            {
773              if (mFacesBridge == null)
774              {
775                mFacesBridge = mFacesBridgeClass.newInstance();
776                mFacesBridge.init(getPortletConfig());
777              }
778            }
779          }
780          catch (Exception e)
781          {
782            throw new PortletException("doBridgeDisptach:  error instantiating the bridge class", e);
783          }
784        }
785      }
786    
787      private String getFromServicesPath(PortletContext context, String resourceName)
788      {
789        // Check for a services definition
790        String result = null;
791        BufferedReader reader = null;
792        InputStream stream = null;
793        try
794        {
795          ClassLoader cl = Thread.currentThread().getContextClassLoader();
796          if (cl == null)
797          {
798            return null;
799          }
800    
801          stream = cl.getResourceAsStream(resourceName);
802          if (stream != null)
803          {
804            // Deal with systems whose native encoding is possibly
805            // different from the way that the services entry was created
806            try
807            {
808              reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
809            } catch (UnsupportedEncodingException e)
810            {
811              reader = new BufferedReader(new InputStreamReader(stream));
812            }
813            result = reader.readLine();
814            if (result != null)
815            {
816              result = result.trim();
817            }
818            reader.close();
819            reader = null;
820            stream = null;
821          }
822        } catch (IOException e)
823        {
824        } catch (SecurityException e)
825        {
826        } finally
827        {
828          if (reader != null)
829          {
830            try
831            {
832              reader.close();
833              stream = null;
834            } catch (Throwable t)
835            {
836              ;
837            }
838            reader = null;
839          }
840          if (stream != null)
841          {
842            try
843            {
844              stream.close();
845            } catch (Throwable t)
846            {
847              ;
848            }
849            stream = null;
850          }
851        }
852        return result;
853      }
854    
855    }