Subversion Repositories general

Rev

Rev 960 | Rev 999 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
956 dev 1
package ak.backpath;
2
 
3
import java.io.InputStream;
4
import java.io.OutputStream;
5
import java.io.ByteArrayInputStream;
6
import java.io.ByteArrayOutputStream;
7
import java.io.IOException;
8
import java.util.Arrays;
9
import java.util.List;
10
import java.util.Iterator;
11
import java.util.Map;
12
import java.util.ArrayList;
13
import java.util.Set;
14
import java.util.TreeSet;
15
import java.util.Collections;
16
import java.util.zip.Deflater;
17
import java.util.zip.Inflater;
18
import java.util.zip.DeflaterOutputStream;
19
import java.util.zip.InflaterInputStream;
20
import java.net.URL;
21
import java.net.URLEncoder;
22
import javax.servlet.http.HttpServletRequest;
23
import org.apache.commons.logging.Log;
24
import org.apache.commons.logging.LogFactory;
25
 
26
public class BackPath
27
{
28
	private static Log log = LogFactory.getLog(BackPath.class);
29
 
30
	public static final String   DEFAULT_KEY            = BackPath.class.getName();
31
	public static final String   DEFAULT_PARAM          = "backpath";
32
	public static final String   OUT_CHARSET            = "UTF-8";
33
	public static final String   URL_CHARSET            = "UTF-8";
34
	public static final String   IGNORES_DELIMITER      = "\\s*;\\s*";
35
	public static final boolean  DEFAULT_ZIP            = true;
36
	public static final String[] METHODS_TO_SAVE_PARAMS = {"GET"};
37
	public static final char     URL_PARAM_START        = '?';
993 dev 38
	public static final String   URL_PARAM_SEPARATOR    = "&";
956 dev 39
	public static final char     URL_PARAM_VALUE        = '=';
40
	public static final char     URL_PARAM_VALUE_SEP    = ',';
41
 
42
	protected static final byte   URL_DELIMITER         = 0;
43
	protected static final int    BUFFER_SIZE           = 1024;
44
	protected static final int    INIT_STREAM_SIZE_COEF = 100;
45
	protected static final String URL_PARAM_START_STR   = "" + URL_PARAM_START;
46
 
47
	protected static Set methodsToSaveParams;
48
 
49
	// List(String)
50
	protected List     stack = new ArrayList();
51
	protected String   param = DEFAULT_PARAM;
52
    protected String[] ignoreParams = null;
53
 
54
	/** zip the stack */
55
	protected boolean zip = DEFAULT_ZIP;
56
 
57
	// string cache
58
	protected String  forwardParams;
59
	protected boolean forwardParamsFormed  = false;
60
	protected String  currentParams;
61
	protected boolean currentParamsFormed  = false;
62
	protected String  backwardParams;
63
	protected boolean backwardParamsFormed = false;
64
	protected String  backwardUrl;
65
	protected boolean backwardUrlFormed    = false;
66
 
67
    static {
68
		methodsToSaveParams = Collections.unmodifiableSet(
69
			new TreeSet(Arrays.asList(METHODS_TO_SAVE_PARAMS)));
70
    }
71
 
72
	protected static Deflater deflater = new Deflater();
73
	protected static Inflater inflater = new Inflater();
74
 
75
	public static BackPath findBackPath(HttpServletRequest request)
76
		throws Exception
77
	{
78
		return findBackPath(request, DEFAULT_KEY, DEFAULT_PARAM, (String)null, DEFAULT_ZIP);
79
	}
80
 
81
	public static BackPath findBackPath(HttpServletRequest request, String backPathKey,
82
			String backPathParam, String backPathIgnore, boolean zip)
83
		throws Exception
84
	{
85
		Object backPathObj = request.getAttribute(backPathKey);
86
 
87
		if(backPathObj == null) {
88
			BackPath backPath;
89
			String[] ignores = null;
90
 
91
            if(backPathIgnore != null)
92
            	ignores = backPathIgnore.trim().split(IGNORES_DELIMITER);
93
 
94
			backPath = new BackPath(request, backPathParam, ignores, zip);
95
			request.setAttribute(backPathKey, backPath);
96
			return backPath;
97
		}
98
		else if(backPathObj instanceof BackPath) {
99
			return (BackPath)backPathObj;
100
		}
101
		else {
102
			throw new Exception("An object under key " + backPathKey
103
				+ " is not a " + BackPath.class.getName());
104
		}
105
	}
106
 
107
	public BackPath(HttpServletRequest request, String param, String[] ignoreParams, boolean zip)
108
	{
109
		this.zip          = zip;
110
		this.param        = param;
111
		this.ignoreParams = ignoreParams;
112
		init(request);
113
	}
114
 
115
	public boolean getZip()
116
	{
117
		return zip;
118
	}
119
 
120
	public void setZip(boolean zip)
121
	{
122
		this.zip = zip;
123
		resetCachedStrings();
124
	}
125
 
126
	public String getParam()
127
	{
128
		return param;
129
	}
130
 
131
	public void setParam(String param)
132
	{
133
		this.param = param;
134
		resetCachedStrings();
135
	}
136
 
137
	public String[] getIgnoreParams()
138
	{
139
		return ignoreParams;
140
	}
141
 
142
	public void setIgnoreParams(String[] ignoreParams)
143
	{
144
		this.ignoreParams = ignoreParams;
145
		resetCachedStrings();
146
	}
147
 
148
	public boolean getHasBack()
149
	{
150
		return (stack.size() > 1);
151
	}
152
 
153
	// List(String)
154
	public List getStack()
155
	{
156
		return Collections.unmodifiableList(stack);
157
	}
158
 
159
	public String getForwardParams()
160
	{
161
		if(!forwardParamsFormed) {
162
			forwardParams       = formForwardParams();
163
			forwardParamsFormed = true;
164
		}
165
 
166
		return forwardParams;
167
	}
168
 
169
	protected String formForwardParams()
170
	{
171
		try {
172
			return encode(stack, zip, 0);
173
		}
174
		catch(IOException ex) {
175
			log.error("Cannot form forward params", ex);
176
			return null;
177
		}
178
	}
179
 
180
	public String getBackwardParams()
181
	{
182
		if(!backwardParamsFormed) {
183
			backwardParams       = formBackwardParams();
184
			backwardParamsFormed = true;
185
		}
186
 
187
		return backwardParams;
188
	}
189
 
190
	protected String formBackwardParams()
191
	{
192
		try {
193
			if(stack.size() <= 2)
194
				return null;
195
			else
196
				return encode(stack, zip, 2);
197
		}
198
		catch(IOException ex) {
199
			log.error("Cannot form backward params", ex);
200
			return null;
201
		}
202
	}
203
 
204
	public String getBackwardUrl()
205
	{
206
		if(!backwardParamsFormed) {
207
			backwardUrl       = formBackwardUrl();
208
			backwardUrlFormed = true;
209
		}
210
 
211
		return backwardUrl;
212
	}
213
 
214
	protected String formBackwardUrl()
215
	{
216
		if(stack.size() < 2) return null;
217
 
218
		try {
219
			StringBuffer url = new StringBuffer((String)stack.get(stack.size()-2));
220
			if(stack.size() > 2) {
221
				String s = encode(stack, zip, 2);
222
				if(url.indexOf(URL_PARAM_START_STR) >= 0) url.append(URL_PARAM_SEPARATOR);
223
				else                                      url.append(URL_PARAM_START);
224
				url.append(param).append(URL_PARAM_VALUE).append(s);
225
			}
226
 
227
			return url.toString();
228
		}
229
		catch(IOException ex) {
230
			log.error("Cannot form backward url", ex);
231
			return null;
232
		}
233
	}
234
 
235
	public String getCurrentParams()
236
	{
237
		if(!currentParamsFormed) {
238
			currentParams       = formCurrentParams();
239
			currentParamsFormed = true;
240
		}
241
 
242
		return currentParams;
243
	}
244
 
245
	protected String formCurrentParams()
246
	{
247
		try {
248
			if(stack.size() <= 1)
249
				return null;
250
			else
251
				return encode(stack, zip, 1);
252
		}
253
		catch(IOException ex) {
254
			log.error("Cannot form current params", ex);
255
			return null;
256
		}
257
	}
258
 
259
	public void init(HttpServletRequest request)
260
	{
261
		try {
262
			resetCachedStrings();
263
			// get old
264
			decode(stack, request.getParameter(param), zip);
265
 
266
			// add new
267
			String url = (new URL(request.getRequestURL().toString())).getPath();
268
			if(methodsToSaveParams.contains(request.getMethod())) {
269
				// to ignore
270
				Set ignore = new TreeSet();
271
				if(ignoreParams != null) {
272
					for(int i = 0; i < ignoreParams.length; i++)
273
						ignore.add(ignoreParams[i]);
274
				}
275
				ignore.add(param);
276
 
277
				// form query string
278
				StringBuffer query = new StringBuffer();
279
				Map requestParams = request.getParameterMap();
280
				for(Iterator i = requestParams.keySet().iterator(); i.hasNext(); ) {
281
					String name = (String)i.next();
282
					if(ignore.contains(name)) continue;
283
					if(query.length() == 0)
284
						query.append(URL_PARAM_START);
285
					else
286
						query.append(URL_PARAM_SEPARATOR);
287
 
288
					query.append(URLEncoder.encode(name, URL_CHARSET));
289
					query.append(URL_PARAM_VALUE);
290
					String[] values = (String[])requestParams.get(name);
291
					for(int j = 0; j < values.length; j++) {
292
						if(j > 0) query.append(URL_PARAM_VALUE_SEP);
293
						query.append(URLEncoder.encode(values[j], URL_CHARSET));
294
					}
295
				}
296
 
297
				// form url
298
				if(query.length() > 0) url = query.insert(0, url).toString();
299
			}
300
			stack.add(url);
301
		}
302
		catch(IOException ex) {
303
			log.error("Cannot init", ex);
304
		}
305
	}
306
 
307
	/**
308
	 * @param stack List(String)
309
	 */
310
	protected String encode(List stack, boolean zip, int suffix)
311
		throws IOException
312
	{
313
		ByteArrayOutputStream buf = new ByteArrayOutputStream(stack.size()*INIT_STREAM_SIZE_COEF);
314
		Object lock = (zip ? (Object)BackPath.class : this);
315
 
316
		synchronized(lock) {
317
			OutputStream out;
318
			if(zip) {
319
				deflater.reset();
320
				out = new DeflaterOutputStream(new Base64.OutputStream(buf), deflater);
321
			}
322
			else {
323
				out = new Base64.OutputStream(buf);
324
			}
325
 
326
			for(int i = 0; i < stack.size()-suffix; i++) {
327
				String s = (String)stack.get(i);
328
				if(i > 0) out.write(URL_DELIMITER);
329
				out.write(s.getBytes(URL_CHARSET));
330
			}
331
			out.close();
332
		}
333
 
334
		return buf.toString(OUT_CHARSET);
335
	}
336
 
337
	/**
338
	 * @param stack List(String)
339
	 */
340
	protected void decode(List stack, String path, boolean zip)
341
		throws IOException
342
	{
343
		stack.clear();
344
		if(path == null || path.equals("")) return;
345
 
346
		ByteArrayOutputStream dec = new ByteArrayOutputStream(stack.size()*INIT_STREAM_SIZE_COEF);
347
		Object lock = (zip ? (Object)BackPath.class : this);
348
 
349
		synchronized(lock) {
350
			ByteArrayInputStream enc = new ByteArrayInputStream(path.getBytes(OUT_CHARSET));
351
 
352
			InputStream in;
353
			if(zip) {
354
				inflater.reset();
355
				in = new InflaterInputStream(new Base64.InputStream(enc), inflater);
356
			}
357
			else {
358
				in = new Base64.InputStream(enc);
359
			}
360
 
361
			byte[] tmp = new byte[BUFFER_SIZE];
362
			int    l;
363
			while((l = in.read(tmp, 0, tmp.length)) > 0) {
364
				dec.write(tmp, 0, l);
365
			}
366
		}
367
 
368
		try {
369
			byte[] buf = dec.toByteArray();
370
			for(int start = 0, end = 0; end <= buf.length; end++) {
371
				if(end == buf.length || buf[end] == URL_DELIMITER) {
372
					stack.add(new String(buf, start, end-start, URL_CHARSET));
373
					start = end+1;
374
				}
375
			}
376
		}
377
		catch(Exception ex) {  // if some mistake in stack, then ignore it completely
378
			stack.clear();
379
			log.info("Cannot parse stack", ex);
380
		}
381
	}
382
 
383
	protected void resetCachedStrings()
384
	{
385
		forwardParams        = null;
386
		forwardParamsFormed  = false;
387
		currentParams        = null;
388
		currentParamsFormed  = false;
389
		backwardParams       = null;
390
		backwardParamsFormed = false;
391
		backwardUrl          = null;
392
		backwardUrlFormed    = false;
393
	}
394
}