View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.orchestra.urlParamNav;
20  
21  import javax.faces.FacesException;
22  import javax.faces.application.ViewHandler;
23  import javax.faces.component.UIViewRoot;
24  import javax.faces.context.FacesContext;
25  import javax.faces.el.ValueBinding;
26  
27  import java.io.IOException;
28  import java.util.Locale;
29  
30  /**
31   * Allow the to-view-id URL in a faces-config navigation case to include
32   * query parameters and EL expressions.
33   * <p>
34   * This class plays a few tricks to hide from the real NavigationHandler
35   * and ViewHandler classes the fact that a URL contains non-standard data.
36   */
37  public class UrlParameterViewHandler extends ViewHandler
38  {
39      private final ViewHandler original;
40  
41      public UrlParameterViewHandler(final ViewHandler original)
42      {
43          this.original = original;
44      }
45  
46      public Locale calculateLocale(FacesContext context)
47      {
48          return original.calculateLocale(context);
49      }
50  
51      public String calculateRenderKitId(FacesContext context)
52      {
53          return original.calculateRenderKitId(context);
54      }
55  
56      public UIViewRoot createView(FacesContext context, String viewId)
57      {
58          return original.createView(context, viewId);
59      }
60  
61      public String getActionURL(FacesContext context, String viewId)
62      {
63          if (viewId != null)
64          {
65              // Expand any EL expression in the URL.
66              //
67              // This handles a call from a NavigationHandler which is processing a redirect
68              // navigation case. A NavigationHandler must call the following in order:
69              //  * ViewHandler.getActionURL,
70              //  * ExternalContext.encodeActionURL
71              //  * ExternalContext.redirect
72              //
73              // Orchestra hooks into ExternalContext.encodeActionURL to trigger the
74              // RequestParameterProviderManager whch then inserts various query params
75              // into the URL.
76              //
77              // So here, ensure that any EL expressions are expanded before the
78              // RequestParameterProviderManager is invoked. An alternative would be for
79              // the RequestParameterProviderManager to do the encoding, but at the current
80              // time that class is not JSF-dependent in any way, so calling JSF expression
81              // expansion from there is not possible.
82              //
83              // Note that this method is also called from a Form component when rendering
84              // its 'action' attribute. This code therefore has the side-effect of
85              // permitting EL expressions in a form's action. This is not particularly
86              // useful, however, as they are expected to have been expanded before this
87              // method is invoked.. 
88              viewId = expandExpressions(context, viewId);
89  
90              // Hide query parameters from the standard ViewHandlerImpl. The standard
91              // implementation of ViewHandlerImpl.getActionUrl method does not handle
92              // query params well. So strip them off, invoke the processing, then reattach
93              // them afterwards.
94              int pos = viewId.indexOf('?');
95              if (pos > -1)
96              {
97                  String realViewId = viewId.substring(0, pos);
98                  String params = viewId.substring(pos);
99  
100                 return original.getActionURL(context, realViewId) + params;
101             }
102         }
103         return original.getActionURL(context, viewId);
104     }
105 
106     public String getResourceURL(FacesContext context, String path)
107     {
108         return original.getResourceURL(context, path);
109     }
110 
111     public void renderView(FacesContext context, UIViewRoot viewToRender)
112         throws IOException, FacesException
113     {
114         original.renderView(context, viewToRender);
115     }
116 
117     public UIViewRoot restoreView(FacesContext context, String viewId)
118     {
119         return original.restoreView(context, viewId);
120     }
121 
122     public void writeState(FacesContext context)
123         throws IOException
124     {
125         original.writeState(context);
126     }
127 
128     private static String expandExpressions(FacesContext context, String url)
129     {
130         int pos = url.indexOf("#{");
131         if (pos > -1 && url.indexOf("}", pos) > -1)
132         {
133             // There is at least one EL expression, so evaluate the whole url string.
134             // Note that something like "aaa#{foo}bbb#{bar}ccc" is fine; both the
135             // el expressions will get replaced.
136             ValueBinding vb = context.getApplication().createValueBinding(url);
137             return (String) vb.getValue(context);
138         }
139 
140         return url;
141     }
142 }