0,0 → 1,394 |
package ak.backpath; |
|
import java.io.InputStream; |
import java.io.OutputStream; |
import java.io.ByteArrayInputStream; |
import java.io.ByteArrayOutputStream; |
import java.io.IOException; |
import java.util.Arrays; |
import java.util.List; |
import java.util.Iterator; |
import java.util.Map; |
import java.util.ArrayList; |
import java.util.Set; |
import java.util.TreeSet; |
import java.util.Collections; |
import java.util.zip.Deflater; |
import java.util.zip.Inflater; |
import java.util.zip.DeflaterOutputStream; |
import java.util.zip.InflaterInputStream; |
import java.net.URL; |
import java.net.URLEncoder; |
import javax.servlet.http.HttpServletRequest; |
import org.apache.commons.logging.Log; |
import org.apache.commons.logging.LogFactory; |
|
public class BackPath |
{ |
private static Log log = LogFactory.getLog(BackPath.class); |
|
public static final String DEFAULT_KEY = BackPath.class.getName(); |
public static final String DEFAULT_PARAM = "backpath"; |
public static final String OUT_CHARSET = "UTF-8"; |
public static final String URL_CHARSET = "UTF-8"; |
public static final String IGNORES_DELIMITER = "\\s*;\\s*"; |
public static final boolean DEFAULT_ZIP = true; |
public static final String[] METHODS_TO_SAVE_PARAMS = {"GET"}; |
public static final char URL_PARAM_START = '?'; |
public static final char URL_PARAM_SEPARATOR = '&'; |
public static final char URL_PARAM_VALUE = '='; |
public static final char URL_PARAM_VALUE_SEP = ','; |
|
protected static final byte URL_DELIMITER = 0; |
protected static final int BUFFER_SIZE = 1024; |
protected static final int INIT_STREAM_SIZE_COEF = 100; |
protected static final String URL_PARAM_START_STR = "" + URL_PARAM_START; |
|
protected static Set methodsToSaveParams; |
|
// List(String) |
protected List stack = new ArrayList(); |
protected String param = DEFAULT_PARAM; |
protected String[] ignoreParams = null; |
|
/** zip the stack */ |
protected boolean zip = DEFAULT_ZIP; |
|
// string cache |
protected String forwardParams; |
protected boolean forwardParamsFormed = false; |
protected String currentParams; |
protected boolean currentParamsFormed = false; |
protected String backwardParams; |
protected boolean backwardParamsFormed = false; |
protected String backwardUrl; |
protected boolean backwardUrlFormed = false; |
|
static { |
methodsToSaveParams = Collections.unmodifiableSet( |
new TreeSet(Arrays.asList(METHODS_TO_SAVE_PARAMS))); |
} |
|
protected static Deflater deflater = new Deflater(); |
protected static Inflater inflater = new Inflater(); |
|
public static BackPath findBackPath(HttpServletRequest request) |
throws Exception |
{ |
return findBackPath(request, DEFAULT_KEY, DEFAULT_PARAM, (String)null, DEFAULT_ZIP); |
} |
|
public static BackPath findBackPath(HttpServletRequest request, String backPathKey, |
String backPathParam, String backPathIgnore, boolean zip) |
throws Exception |
{ |
Object backPathObj = request.getAttribute(backPathKey); |
|
if(backPathObj == null) { |
BackPath backPath; |
String[] ignores = null; |
|
if(backPathIgnore != null) |
ignores = backPathIgnore.trim().split(IGNORES_DELIMITER); |
|
backPath = new BackPath(request, backPathParam, ignores, zip); |
request.setAttribute(backPathKey, backPath); |
return backPath; |
} |
else if(backPathObj instanceof BackPath) { |
return (BackPath)backPathObj; |
} |
else { |
throw new Exception("An object under key " + backPathKey |
+ " is not a " + BackPath.class.getName()); |
} |
} |
|
public BackPath(HttpServletRequest request, String param, String[] ignoreParams, boolean zip) |
{ |
this.zip = zip; |
this.param = param; |
this.ignoreParams = ignoreParams; |
init(request); |
} |
|
public boolean getZip() |
{ |
return zip; |
} |
|
public void setZip(boolean zip) |
{ |
this.zip = zip; |
resetCachedStrings(); |
} |
|
public String getParam() |
{ |
return param; |
} |
|
public void setParam(String param) |
{ |
this.param = param; |
resetCachedStrings(); |
} |
|
public String[] getIgnoreParams() |
{ |
return ignoreParams; |
} |
|
public void setIgnoreParams(String[] ignoreParams) |
{ |
this.ignoreParams = ignoreParams; |
resetCachedStrings(); |
} |
|
public boolean getHasBack() |
{ |
return (stack.size() > 1); |
} |
|
// List(String) |
public List getStack() |
{ |
return Collections.unmodifiableList(stack); |
} |
|
public String getForwardParams() |
{ |
if(!forwardParamsFormed) { |
forwardParams = formForwardParams(); |
forwardParamsFormed = true; |
} |
|
return forwardParams; |
} |
|
protected String formForwardParams() |
{ |
try { |
return encode(stack, zip, 0); |
} |
catch(IOException ex) { |
log.error("Cannot form forward params", ex); |
return null; |
} |
} |
|
public String getBackwardParams() |
{ |
if(!backwardParamsFormed) { |
backwardParams = formBackwardParams(); |
backwardParamsFormed = true; |
} |
|
return backwardParams; |
} |
|
protected String formBackwardParams() |
{ |
try { |
if(stack.size() <= 2) |
return null; |
else |
return encode(stack, zip, 2); |
} |
catch(IOException ex) { |
log.error("Cannot form backward params", ex); |
return null; |
} |
} |
|
public String getBackwardUrl() |
{ |
if(!backwardParamsFormed) { |
backwardUrl = formBackwardUrl(); |
backwardUrlFormed = true; |
} |
|
return backwardUrl; |
} |
|
protected String formBackwardUrl() |
{ |
if(stack.size() < 2) return null; |
|
try { |
StringBuffer url = new StringBuffer((String)stack.get(stack.size()-2)); |
if(stack.size() > 2) { |
String s = encode(stack, zip, 2); |
if(url.indexOf(URL_PARAM_START_STR) >= 0) url.append(URL_PARAM_SEPARATOR); |
else url.append(URL_PARAM_START); |
url.append(param).append(URL_PARAM_VALUE).append(s); |
} |
|
return url.toString(); |
} |
catch(IOException ex) { |
log.error("Cannot form backward url", ex); |
return null; |
} |
} |
|
public String getCurrentParams() |
{ |
if(!currentParamsFormed) { |
currentParams = formCurrentParams(); |
currentParamsFormed = true; |
} |
|
return currentParams; |
} |
|
protected String formCurrentParams() |
{ |
try { |
if(stack.size() <= 1) |
return null; |
else |
return encode(stack, zip, 1); |
} |
catch(IOException ex) { |
log.error("Cannot form current params", ex); |
return null; |
} |
} |
|
public void init(HttpServletRequest request) |
{ |
try { |
resetCachedStrings(); |
// get old |
decode(stack, request.getParameter(param), zip); |
|
// add new |
String url = (new URL(request.getRequestURL().toString())).getPath(); |
if(methodsToSaveParams.contains(request.getMethod())) { |
// to ignore |
Set ignore = new TreeSet(); |
if(ignoreParams != null) { |
for(int i = 0; i < ignoreParams.length; i++) |
ignore.add(ignoreParams[i]); |
} |
ignore.add(param); |
|
// form query string |
StringBuffer query = new StringBuffer(); |
Map requestParams = request.getParameterMap(); |
for(Iterator i = requestParams.keySet().iterator(); i.hasNext(); ) { |
String name = (String)i.next(); |
if(ignore.contains(name)) continue; |
if(query.length() == 0) |
query.append(URL_PARAM_START); |
else |
query.append(URL_PARAM_SEPARATOR); |
|
query.append(URLEncoder.encode(name, URL_CHARSET)); |
query.append(URL_PARAM_VALUE); |
String[] values = (String[])requestParams.get(name); |
for(int j = 0; j < values.length; j++) { |
if(j > 0) query.append(URL_PARAM_VALUE_SEP); |
query.append(URLEncoder.encode(values[j], URL_CHARSET)); |
} |
} |
|
// form url |
if(query.length() > 0) url = query.insert(0, url).toString(); |
} |
stack.add(url); |
} |
catch(IOException ex) { |
log.error("Cannot init", ex); |
} |
} |
|
/** |
* @param stack List(String) |
*/ |
protected String encode(List stack, boolean zip, int suffix) |
throws IOException |
{ |
ByteArrayOutputStream buf = new ByteArrayOutputStream(stack.size()*INIT_STREAM_SIZE_COEF); |
Object lock = (zip ? (Object)BackPath.class : this); |
|
synchronized(lock) { |
OutputStream out; |
if(zip) { |
deflater.reset(); |
out = new DeflaterOutputStream(new Base64.OutputStream(buf), deflater); |
} |
else { |
out = new Base64.OutputStream(buf); |
} |
|
for(int i = 0; i < stack.size()-suffix; i++) { |
String s = (String)stack.get(i); |
if(i > 0) out.write(URL_DELIMITER); |
out.write(s.getBytes(URL_CHARSET)); |
} |
out.close(); |
} |
|
return buf.toString(OUT_CHARSET); |
} |
|
/** |
* @param stack List(String) |
*/ |
protected void decode(List stack, String path, boolean zip) |
throws IOException |
{ |
stack.clear(); |
if(path == null || path.equals("")) return; |
|
ByteArrayOutputStream dec = new ByteArrayOutputStream(stack.size()*INIT_STREAM_SIZE_COEF); |
Object lock = (zip ? (Object)BackPath.class : this); |
|
synchronized(lock) { |
ByteArrayInputStream enc = new ByteArrayInputStream(path.getBytes(OUT_CHARSET)); |
|
InputStream in; |
if(zip) { |
inflater.reset(); |
in = new InflaterInputStream(new Base64.InputStream(enc), inflater); |
} |
else { |
in = new Base64.InputStream(enc); |
} |
|
byte[] tmp = new byte[BUFFER_SIZE]; |
int l; |
while((l = in.read(tmp, 0, tmp.length)) > 0) { |
dec.write(tmp, 0, l); |
} |
} |
|
try { |
byte[] buf = dec.toByteArray(); |
for(int start = 0, end = 0; end <= buf.length; end++) { |
if(end == buf.length || buf[end] == URL_DELIMITER) { |
stack.add(new String(buf, start, end-start, URL_CHARSET)); |
start = end+1; |
} |
} |
} |
catch(Exception ex) { // if some mistake in stack, then ignore it completely |
stack.clear(); |
log.info("Cannot parse stack", ex); |
} |
} |
|
protected void resetCachedStrings() |
{ |
forwardParams = null; |
forwardParamsFormed = false; |
currentParams = null; |
currentParamsFormed = false; |
backwardParams = null; |
backwardParamsFormed = false; |
backwardUrl = null; |
backwardUrlFormed = false; |
} |
} |