/*
* Copyright 2009-2010 the original author or authors.
*
* Licensed 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.springframework.batch.admin.web.filter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
public class ParameterUnpackerFilter extends OncePerRequestFilter {
private String prefix = "unpack_";
private String delimiter = "|";
private boolean putEmptyParamsInPath = false;
/**
* Flag to say that empty parameter value signal their name should be used
* as a path parameter. E.g. <code>/context/path?unpack_foo</code> goes to
* <code>context/path/foo</code>.
*
* @param putEmptyParamsInPath the flag value to set (defaults to false)
*/
public void setPutEmptyParamsInPath(boolean putEmptyParamsInPath) {
this.putEmptyParamsInPath = putEmptyParamsInPath;
}
/**
* @param delimiter the delimiter used to separate parameter names from
* values
*/
public void setDelimiter(String delimiter) {
this.delimiter = delimiter;
}
/**
* @param prefix the prefix for parameter names that need to be unpacked
*/
public void setPrefix(String prefix) {
this.prefix = prefix;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
Map<String, String[]> unpacked = new HashMap<String, String[]>();
List<String> pathKeys = new ArrayList<String>();
for (Object key : request.getParameterMap().keySet()) {
String paramName = (String) key;
if (paramName.startsWith(prefix)) {
String embedded = paramName.substring(prefix.length());
unpacked.putAll(unpack(embedded, delimiter));
pathKeys.addAll(extractPathKeys(embedded, delimiter));
}
}
if (unpacked.size() > 0) {
filterChain.doFilter(new EnhancedRequestWrapper(unpacked, request, putEmptyParamsInPath ? pathKeys
: Collections.<String> emptyList()), response);
}
else {
filterChain.doFilter(request, response);
}
}
protected Collection<? extends String> extractPathKeys(String paramName, String delimiter) {
List<String> result = new ArrayList<String>();
String[] values = StringUtils.delimitedListToStringArray(paramName, delimiter);
for (int i = 0; i < (values.length + 1) / 2; i++) {
int j = 2 * i + 1;
String key = values[j - 1];
String next = j < values.length ? values[j].trim() : "";
if (next!=null && next.length()==0) {
result.add(key);
}
}
return result;
}
protected Map<String, String[]> unpack(String paramName, String delimiter) {
Map<String, String[]> unpacked = new HashMap<String, String[]>();
String[] values = StringUtils.delimitedListToStringArray(paramName, delimiter);
for (int i = 0; i < (values.length + 1) / 2; i++) {
int j = 2 * i + 1;
String key = values[j - 1];
String[] saved = unpacked.get(key);
String next = j < values.length ? values[j] : "";
if (saved == null) {
saved = new String[] { next };
}
else {
List<String> list = new ArrayList<String>(Arrays.asList(saved));
list.add(next);
saved = list.toArray(new String[list.size()]);
}
unpacked.put(key, saved);
}
return unpacked;
}
private static class EnhancedRequestWrapper extends HttpServletRequestWrapper {
private final Map<String, String[]> params = new HashMap<String, String[]>();
private final List<String> pathParams;
public EnhancedRequestWrapper(Map<String, String[]> params, HttpServletRequest request, List<String> pathParams) {
super(request);
this.pathParams = pathParams;
this.params.putAll(params);
}
@Override
public String getContextPath() {
String base = super.getContextPath();
if (pathParams!=null && pathParams.isEmpty()) {
return base;
}
StringBuffer buffer = new StringBuffer(base);
for (String key : pathParams) {
buffer.append("/" + key);
}
return buffer.toString();
}
@Override
public Enumeration<String> getParameterNames() {
Set<String> paramNames = new HashSet<String>();
@SuppressWarnings("unchecked")
Enumeration paramEnum = super.getParameterNames();
while (paramEnum.hasMoreElements()) {
paramNames.add((String) paramEnum.nextElement());
}
paramNames.addAll(params.keySet());
return Collections.enumeration(paramNames);
}
@Override
public String getParameter(String name) {
String[] value = params.get(name);
if (value != null) {
return (value.length > 0 ? value[0] : null);
}
return super.getParameter(name);
}
@Override
public String[] getParameterValues(String name) {
String[] value = params.get(name);
if (value != null) {
return value;
}
return super.getParameterValues(name);
}
@Override
@SuppressWarnings("unchecked")
public Map<String, String[]> getParameterMap() {
Map<String, String[]> paramMap = new HashMap<String, String[]>();
paramMap.putAll(super.getParameterMap());
paramMap.putAll(params);
return paramMap;
}
}
}