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  
20  package org.apache.myfaces.orchestra.requestParameterProvider;
21  
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  import org.apache.myfaces.orchestra.frameworkAdapter.FrameworkAdapter;
25  import org.apache.myfaces.orchestra.lib.OrchestraException;
26  
27  import java.io.Serializable;
28  import java.util.ArrayList;
29  import java.util.HashSet;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Set;
33  
34  /**
35   * The manager which manage all the attached providers and add their fields to the url.
36   * <p>
37   * This class has an instance per user http session. Code that wishes to add values
38   * to urls generated within pages register a "provider" with this object. When request
39   * params need to be output this manager invokes each provider in turn.
40   * <p>
41   * If the data accessed by a registered "provider" has scope shorter than an http session
42   * then the registered provider should obviously be <i>deregistered</i> when the data
43   * is no longer valid.
44   * <p>
45   * This class works together with the RequestParameterServletFilter and
46   * RequestParameterResponseWrapper so that every call to response.encodeURL(...) gets
47   * forwarded to this class. As encodeURL is used by JSF commandButton, commandLink, etc,
48   * this ensures that whatever the user clicks on the parameters provided by the
49   * registered provider objects are present on the next JSF request.
50   */
51  public class RequestParameterProviderManager implements Serializable
52  {
53      private final Log LOG = LogFactory.getLog(RequestParameterProviderManager.class);
54  
55      private static final String PAGE_LINK_SEP = "#";
56  
57      private static final Set SCHEMES_TO_EXCLUDE = new HashSet();
58  
59      private static final String PAGE_PARAMETER_SEP = "?";
60      private static final String PARAMETER_SEP = "&";
61      private static final String PARAMETER_PROVIDER_MANAGER_KEY = RequestParameterProviderManager.class.getName();
62  
63      // TODO: investigate why this is transient. At least some callers of register call
64      // it only once per session, so if the session data is passed to another machine or is
65      // saved then restored then it seems the registered providers will be lost when they
66      // should not be...
67      private transient List providers;
68  
69      static
70      {
71          SCHEMES_TO_EXCLUDE.add("javascript");
72          SCHEMES_TO_EXCLUDE.add("ftp");
73          SCHEMES_TO_EXCLUDE.add("mailto");
74      }
75  
76      private RequestParameterProviderManager()
77      {
78      }
79  
80      public static RequestParameterProviderManager getInstance()
81      {
82          FrameworkAdapter fa = FrameworkAdapter.getCurrentInstance();
83          if (fa == null)
84          {
85              Log log = LogFactory.getLog(RequestParameterProviderManager.class);
86              log.error("No framework adapter currently selected");
87              throw new OrchestraException("No framework adapter currently selected");
88          }
89          RequestParameterProviderManager manager =
90              (RequestParameterProviderManager) fa.getSessionAttribute(PARAMETER_PROVIDER_MANAGER_KEY);
91  
92          if (manager == null)
93          {
94              // TODO: remove this factory code. Not IOC-friendly.
95              manager = new RequestParameterProviderManager();
96              fa.setSessionAttribute(PARAMETER_PROVIDER_MANAGER_KEY, manager);
97          }
98  
99          return manager;
100     }
101 
102 
103     /**
104      * Register the given provider.
105      *
106      * @param provider the provider to register.
107      */
108 
109     public void register(RequestParameterProvider provider)
110     {
111         if (provider == null)
112         {
113             LOG.warn("RequestParameterProvider is null -> no registration!");
114         }
115         else
116         {
117             getProviders().add(provider);
118         }
119     }
120 
121 
122     /**
123      * Encode all fields of all providers, and attach the name-value pairs to url.
124      *
125      * @param url the URL to which the fields should be attached.
126      * @return the url after attaching all fields.
127      */
128 
129     public String encodeAndAttachParameters(String url)
130     {
131         if (!isResponseIntercepted())
132         {
133             throw new IllegalStateException(
134                 "RequestParameterServletFilter not called. Please configure the filter "
135                     + RequestParameterServletFilter.class.getName()
136                     + " in your web.xml to cover your faces requests.");
137         }
138 
139         if (url == null)
140         {
141             return null;
142         }
143 
144         if (PAGE_LINK_SEP.equals(url))
145         {
146             // just an inner page link, ignore
147             return url;
148         }
149 
150         int pos = url.indexOf(":");
151         if (pos > 0)
152         {
153             String probablyScheme = url.substring(0, pos);
154             if (SCHEMES_TO_EXCLUDE.contains(probablyScheme))
155             {
156                 // a scheme we know to not encode to
157                 return url;
158             }
159         }
160 
161         StringBuffer sb = new StringBuffer();
162 
163         int nuofParams = -1;
164         String firstSeparator = url.indexOf(PAGE_PARAMETER_SEP) == -1 ? PAGE_PARAMETER_SEP : PARAMETER_SEP;
165         int pageLinkPos = url.indexOf(PAGE_LINK_SEP);
166         if (pageLinkPos < 0)
167         {
168             sb.append(url);
169         }
170         else
171         {
172             sb.append(url.substring(0, pageLinkPos));
173         }
174 
175         for (Iterator it = getProviders().iterator(); it.hasNext();)
176         {
177             RequestParameterProvider provider = (RequestParameterProvider) it.next();
178             String[] fields = provider.getFields();
179             if (fields == null)
180             {
181                 continue;
182             }
183             for (int i = 0; i < fields.length; i++)
184             {
185                 nuofParams++;
186 
187                 // skip adding this parameter if already found in the URL.
188                 // For instance, to avoid adding multiple conversationContext if the
189                 // encodeURL method is invoked multiple times.
190                 if (url.indexOf(fields[i]) > 0)
191                 {
192                     continue;
193                 }
194 
195                 sb.append(nuofParams == 0 ? firstSeparator : PARAMETER_SEP);
196                 sb.append(fields[i]);
197                 sb.append("=");
198                 sb.append(provider.getFieldValue(fields[i]));
199             }
200         }
201 
202         if (pageLinkPos > -1)
203         {
204             sb.append(url.substring(pageLinkPos));
205         }
206         return sb.toString();
207     }
208 
209     protected boolean isResponseIntercepted()
210     {
211         FrameworkAdapter fa = FrameworkAdapter.getCurrentInstance();
212 
213         return Boolean.TRUE.equals(
214                 fa.getRequestAttribute(RequestParameterServletFilter.REQUEST_PARAM_FILTER_CALLED))
215             || Boolean.TRUE.equals(
216                 fa.getRequestAttribute(RequestParameterServletFilter.REQUEST_PARAM_RESPONSE_WRAPPED));
217     }
218 
219     protected List getProviders()
220     {
221         if (providers == null)
222         {
223             providers = new ArrayList();
224         }
225 
226         return providers;
227     }
228 }