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.application.ViewHandler;
22 import javax.faces.application.ViewHandlerWrapper;
23 import javax.faces.context.FacesContext;
24 import javax.faces.el.ValueBinding;
25
26 /**
27 * Allow the to-view-id URL in a faces-config navigation case to include
28 * query parameters and EL expressions.
29 * <p>
30 * This class plays a few tricks to hide from the real NavigationHandler
31 * and ViewHandler classes the fact that a URL contains non-standard data.
32 * <p>
33 * This class also plays a few reflection-based tricks so that the code can
34 * be compiled against JSF1.1, and work with both JSF1.1 and JSF1.2. The
35 * code is a little fragile and will probably need to be updated to work
36 * correctly with JSF2.0, but that is the fault of the JSF spec.
37 */
38 public class UrlParameterViewHandler extends ViewHandlerWrapper
39 {
40 private final ViewHandler original;
41
42
43
44 /**
45 * Constructor.
46 */
47 public UrlParameterViewHandler(final ViewHandler original)
48 {
49 this.original = original;
50 }
51
52 public ViewHandler getWrapped()
53 {
54 return original;
55 }
56
57 public String getActionURL(FacesContext context, String viewId)
58 {
59 if (viewId != null)
60 {
61 // Expand any EL expression in the URL.
62 //
63 // This handles a call from a NavigationHandler which is processing a redirect
64 // navigation case. A NavigationHandler must call the following in order:
65 // * ViewHandler.getActionURL,
66 // * ExternalContext.encodeActionURL
67 // * ExternalContext.redirect
68 //
69 // Orchestra hooks into ExternalContext.encodeActionURL to trigger the
70 // RequestParameterProviderManager which then inserts various query params
71 // into the URL.
72 //
73 // So here, ensure that any EL expressions are expanded before the
74 // RequestParameterProviderManager is invoked. An alternative would be for
75 // the RequestParameterProviderManager to do the encoding, but at the current
76 // time that class is not JSF-dependent in any way, so calling JSF expression
77 // expansion from there is not possible.
78 //
79 // Note that this method is also called from a Form component when rendering
80 // its 'action' attribute. This code therefore has the side-effect of
81 // permitting EL expressions in a form's action. This is not particularly
82 // useful, however, as they are expected to have been expanded before this
83 // method is invoked..
84 viewId = expandExpressions(context, viewId);
85
86 // Hide query parameters from the standard ViewHandlerImpl. The standard
87 // implementation of ViewHandlerImpl.getActionUrl method does not handle
88 // query params well. So strip them off, invoke the processing, then reattach
89 // them afterwards.
90 int pos = viewId.indexOf('?');
91 if (pos > -1)
92 {
93 String realViewId = viewId.substring(0, pos);
94 String params = viewId.substring(pos);
95
96 return original.getActionURL(context, realViewId) + params;
97 }
98 }
99 return original.getActionURL(context, viewId);
100 }
101
102 private static String expandExpressions(FacesContext context, String url)
103 {
104 int pos = url.indexOf("#{");
105 if (pos > -1 && url.indexOf("}", pos) > -1)
106 {
107 // There is at least one EL expression, so evaluate the whole url string.
108 // Note that something like "aaa#{foo}bbb#{bar}ccc" is fine; both the
109 // el expressions will get replaced.
110 ValueBinding vb = context.getApplication().createValueBinding(url);
111 return (String) vb.getValue(context);
112 }
113
114 return url;
115 }
116 }