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 }