/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.orchestra.requestParameterProvider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.orchestra.frameworkAdapter.FrameworkAdapter;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* The manager which manage all the attached providers and add their fields to the url.
* <p>
* This class has an instance per user http session. Code that wishes to add values
* to urls generated within pages register a "provider" with this object. When request
* params need to be output this manager invokes each provider in turn.
* <p>
* If the data accessed by a registered "provider" has scope shorter than an http session
* then the registered provider should obviously be <i>deregistered</i> when the data
* is no longer valid.
* <p>
* This class works together with the RequestParameterServletFilter and
* RequestParameterResponseWrapper so that every call to response.encodeURL(...) gets
* forwarded to this class. As encodeURL is used by JSF commandButton, commandLink, etc,
* this ensures that whatever the user clicks on the parameters provided by the
* registered provider objects are present on the next JSF request.
*/
public class RequestParameterProviderManager implements Serializable
{
private final Log LOG = LogFactory.getLog(RequestParameterProviderManager.class);
private static final String PAGE_LINK_SEP = "#";
private static final Set SCHEMES_TO_EXCLUDE = new HashSet();
private static final String PAGE_PARAMETER_SEP = "?";
private static final String PARAMETER_SEP = "&";
private static final String PARAMETER_PROVIDER_MANAGER_KEY = RequestParameterProviderManager.class.getName();
// TODO: investigate why this is transient. At least some callers of register call
// it only once per session, so if the session data is passed to another machine or is
// saved then restored then it seems the registered providers will be lost when they
// should not be...
private transient List providers;
static
{
SCHEMES_TO_EXCLUDE.add("javascript");
SCHEMES_TO_EXCLUDE.add("ftp");
SCHEMES_TO_EXCLUDE.add("mailto");
}
private RequestParameterProviderManager()
{
}
public static RequestParameterProviderManager getInstance()
{
RequestParameterProviderManager manager =
(RequestParameterProviderManager) FrameworkAdapter.getCurrentInstance()
.getSessionAttribute(PARAMETER_PROVIDER_MANAGER_KEY);
if (manager == null)
{
// TODO: remove this factory code. Not IOC-friendly.
manager = new RequestParameterProviderManager();
FrameworkAdapter.getCurrentInstance().setSessionAttribute(PARAMETER_PROVIDER_MANAGER_KEY, manager);
}
return manager;
}
/**
* Register the given provider.
*
* @param provider the provider to register.
*/
public void register(RequestParameterProvider provider)
{
if (provider == null)
{
LOG.warn("RequestParameterProvider is null -> no registration!");
}
else
{
getProviders().add(provider);
}
}
/**
* Encode all fields of all providers, and attach the name-value pairs to url.
*
* @param url the URL to which the fields should be attached.
* @return the url after attaching all fields.
*/
public String encodeAndAttachParameters(String url)
{
if (!isResponseIntercepted())
{
throw new IllegalStateException(
"RequestParameterServletFilter not called. Please configure the filter " +
RequestParameterServletFilter.class.getName() +
" in your web.xml to cover your faces requests.");
}
if (url == null)
{
return null;
}
if (PAGE_LINK_SEP.equals(url))
{
// just an inner page link, ignore
return url;
}
int pos = url.indexOf(":");
if (pos > 0)
{
String probablyScheme = url.substring(0, pos);
if (SCHEMES_TO_EXCLUDE.contains(probablyScheme))
{
// a scheme we know to not encode to
return url;
}
}
StringBuffer sb = new StringBuffer();
int nuofParams = -1;
String firstSeparator = url.indexOf(PAGE_PARAMETER_SEP) == -1 ? PAGE_PARAMETER_SEP : PARAMETER_SEP;
int pageLinkPos = url.indexOf(PAGE_LINK_SEP);
if (pageLinkPos < 0)
{
sb.append(url);
}
else
{
sb.append(url.substring(0, pageLinkPos));
}
for (Iterator it = getProviders().iterator(); it.hasNext();)
{
RequestParameterProvider provider = (RequestParameterProvider) it.next();
String[] fields = provider.getFields();
if (fields == null)
{
continue;
}
for (int i = 0; i < fields.length; i++)
{
nuofParams++;
sb.append(nuofParams == 0 ? firstSeparator : PARAMETER_SEP);
sb.append(fields[i]);
sb.append("=");
sb.append(provider.getFieldValue(fields[i]));
}
}
if (pageLinkPos > -1)
{
sb.append(url.substring(pageLinkPos));
}
return sb.toString();
}
protected boolean isResponseIntercepted()
{
FrameworkAdapter fa = FrameworkAdapter.getCurrentInstance();
return Boolean.TRUE.equals(
fa.getRequestAttribute(RequestParameterServletFilter.REQUEST_PARAM_FILTER_CALLED))
|| Boolean.TRUE.equals(
fa.getRequestAttribute(RequestParameterServletFilter.REQUEST_PARAM_RESPONSE_WRAPPED));
}
protected List getProviders()
{
if (providers == null)
{
providers = new ArrayList();
}
return providers;
}
}