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 }