Subversion Repositories general

Rev

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        = '?';
999 dev 38
	public static final char     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;
1026 dev 62
	protected String  currentUrl;
63
	protected boolean currentUrlFormed     = false;
956 dev 64
	protected String  backwardParams;
65
	protected boolean backwardParamsFormed = false;
66
	protected String  backwardUrl;
67
	protected boolean backwardUrlFormed    = false;
68
 
69
    static {
70
		methodsToSaveParams = Collections.unmodifiableSet(
71
			new TreeSet(Arrays.asList(METHODS_TO_SAVE_PARAMS)));
72
    }
73
 
74
	protected static Deflater deflater = new Deflater();
75
	protected static Inflater inflater = new Inflater();
76
 
77
	public static BackPath findBackPath(HttpServletRequest request)
78
		throws Exception
79
	{
80
		return findBackPath(request, DEFAULT_KEY, DEFAULT_PARAM, (String)null, DEFAULT_ZIP);
81
	}
82
 
83
	public static BackPath findBackPath(HttpServletRequest request, String backPathKey,
84
			String backPathParam, String backPathIgnore, boolean zip)
85
		throws Exception
86
	{
1026 dev 87
		String[] ignores = null;
88
 
89
        if(backPathIgnore != null)
90
          	ignores = backPathIgnore.trim().split(IGNORES_DELIMITER);
91
 
92
		return findBackPath(request, backPathKey, backPathParam, ignores, zip);
93
	}
94
 
95
	public static BackPath findBackPath(HttpServletRequest request, String backPathKey,
96
			String backPathParam, String[] ignores, boolean zip)
97
		throws Exception
98
	{
956 dev 99
		Object backPathObj = request.getAttribute(backPathKey);
100
 
101
		if(backPathObj == null) {
102
			BackPath backPath;
103
 
104
			backPath = new BackPath(request, backPathParam, ignores, zip);
105
			request.setAttribute(backPathKey, backPath);
106
			return backPath;
107
		}
108
		else if(backPathObj instanceof BackPath) {
109
			return (BackPath)backPathObj;
110
		}
111
		else {
112
			throw new Exception("An object under key " + backPathKey
113
				+ " is not a " + BackPath.class.getName());
114
		}
115
	}
116
 
117
	public BackPath(HttpServletRequest request, String param, String[] ignoreParams, boolean zip)
118
	{
119
		this.zip          = zip;
120
		this.param        = param;
121
		this.ignoreParams = ignoreParams;
122
		init(request);
123
	}
124
 
125
	public boolean getZip()
126
	{
127
		return zip;
128
	}
129
 
130
	public void setZip(boolean zip)
131
	{
132
		this.zip = zip;
133
		resetCachedStrings();
134
	}
135
 
136
	public String getParam()
137
	{
138
		return param;
139
	}
140
 
141
	public void setParam(String param)
142
	{
143
		this.param = param;
144
		resetCachedStrings();
145
	}
146
 
147
	public String[] getIgnoreParams()
148
	{
149
		return ignoreParams;
150
	}
151
 
152
	public void setIgnoreParams(String[] ignoreParams)
153
	{
154
		this.ignoreParams = ignoreParams;
155
		resetCachedStrings();
156
	}
157
 
158
	public boolean getHasBack()
159
	{
160
		return (stack.size() > 1);
161
	}
162
 
163
	// List(String)
164
	public List getStack()
165
	{
166
		return Collections.unmodifiableList(stack);
167
	}
168
 
169
	public String getForwardParams()
170
	{
171
		if(!forwardParamsFormed) {
172
			forwardParams       = formForwardParams();
173
			forwardParamsFormed = true;
174
		}
175
 
176
		return forwardParams;
177
	}
178
 
179
	protected String formForwardParams()
180
	{
181
		try {
182
			return encode(stack, zip, 0);
183
		}
184
		catch(IOException ex) {
185
			log.error("Cannot form forward params", ex);
186
			return null;
187
		}
188
	}
189
 
190
	public String getBackwardParams()
191
	{
192
		if(!backwardParamsFormed) {
193
			backwardParams       = formBackwardParams();
194
			backwardParamsFormed = true;
195
		}
196
 
197
		return backwardParams;
198
	}
199
 
200
	protected String formBackwardParams()
201
	{
202
		try {
203
			if(stack.size() <= 2)
204
				return null;
205
			else
206
				return encode(stack, zip, 2);
207
		}
208
		catch(IOException ex) {
209
			log.error("Cannot form backward params", ex);
210
			return null;
211
		}
212
	}
213
 
214
	public String getBackwardUrl()
215
	{
216
		if(!backwardParamsFormed) {
217
			backwardUrl       = formBackwardUrl();
218
			backwardUrlFormed = true;
219
		}
220
 
221
		return backwardUrl;
222
	}
223
 
224
	protected String formBackwardUrl()
225
	{
226
		if(stack.size() < 2) return null;
227
 
228
		try {
229
			StringBuffer url = new StringBuffer((String)stack.get(stack.size()-2));
230
			if(stack.size() > 2) {
231
				String s = encode(stack, zip, 2);
232
				if(url.indexOf(URL_PARAM_START_STR) >= 0) url.append(URL_PARAM_SEPARATOR);
233
				else                                      url.append(URL_PARAM_START);
234
				url.append(param).append(URL_PARAM_VALUE).append(s);
235
			}
236
 
237
			return url.toString();
238
		}
239
		catch(IOException ex) {
240
			log.error("Cannot form backward url", ex);
241
			return null;
242
		}
243
	}
244
 
245
	public String getCurrentParams()
246
	{
247
		if(!currentParamsFormed) {
248
			currentParams       = formCurrentParams();
249
			currentParamsFormed = true;
250
		}
251
 
252
		return currentParams;
253
	}
254
 
255
	protected String formCurrentParams()
256
	{
257
		try {
258
			if(stack.size() <= 1)
259
				return null;
260
			else
261
				return encode(stack, zip, 1);
262
		}
263
		catch(IOException ex) {
264
			log.error("Cannot form current params", ex);
265
			return null;
266
		}
267
	}
268
 
1026 dev 269
	public String getCurrentUrl()
270
	{
271
		if(!currentParamsFormed) {
272
			currentUrl       = formCurrentUrl();
273
			currentUrlFormed = true;
274
		}
275
 
276
		return currentUrl;
277
	}
278
 
279
	protected String formCurrentUrl()
280
	{
281
		if(stack.size() < 1) return null;
282
 
283
		try {
284
			StringBuffer url = new StringBuffer((String)stack.get(stack.size()-1));
285
			if(stack.size() > 1) {
286
				String s = encode(stack, zip, 1);
287
				if(url.indexOf(URL_PARAM_START_STR) >= 0) url.append(URL_PARAM_SEPARATOR);
288
				else                                      url.append(URL_PARAM_START);
289
				url.append(param).append(URL_PARAM_VALUE).append(s);
290
			}
291
 
292
			return url.toString();
293
		}
294
		catch(IOException ex) {
295
			log.error("Cannot form current url", ex);
296
			return null;
297
		}
298
	}
299
 
956 dev 300
	public void init(HttpServletRequest request)
301
	{
302
		try {
303
			resetCachedStrings();
304
			// get old
305
			decode(stack, request.getParameter(param), zip);
306
 
307
			// add new
308
			String url = (new URL(request.getRequestURL().toString())).getPath();
309
			if(methodsToSaveParams.contains(request.getMethod())) {
310
				// to ignore
311
				Set ignore = new TreeSet();
312
				if(ignoreParams != null) {
313
					for(int i = 0; i < ignoreParams.length; i++)
314
						ignore.add(ignoreParams[i]);
315
				}
316
				ignore.add(param);
317
 
318
				// form query string
319
				StringBuffer query = new StringBuffer();
320
				Map requestParams = request.getParameterMap();
321
				for(Iterator i = requestParams.keySet().iterator(); i.hasNext(); ) {
322
					String name = (String)i.next();
323
					if(ignore.contains(name)) continue;
324
					if(query.length() == 0)
325
						query.append(URL_PARAM_START);
326
					else
327
						query.append(URL_PARAM_SEPARATOR);
328
 
329
					query.append(URLEncoder.encode(name, URL_CHARSET));
330
					query.append(URL_PARAM_VALUE);
331
					String[] values = (String[])requestParams.get(name);
332
					for(int j = 0; j < values.length; j++) {
333
						if(j > 0) query.append(URL_PARAM_VALUE_SEP);
334
						query.append(URLEncoder.encode(values[j], URL_CHARSET));
335
					}
336
				}
337
 
338
				// form url
339
				if(query.length() > 0) url = query.insert(0, url).toString();
340
			}
1026 dev 341
 
956 dev 342
			stack.add(url);
343
		}
344
		catch(IOException ex) {
345
			log.error("Cannot init", ex);
346
		}
347
	}
348
 
349
	/**
350
	 * @param stack List(String)
351
	 */
352
	protected String encode(List stack, boolean zip, int suffix)
353
		throws IOException
354
	{
355
		ByteArrayOutputStream buf = new ByteArrayOutputStream(stack.size()*INIT_STREAM_SIZE_COEF);
356
		Object lock = (zip ? (Object)BackPath.class : this);
357
 
358
		synchronized(lock) {
359
			OutputStream out;
360
			if(zip) {
361
				deflater.reset();
362
				out = new DeflaterOutputStream(new Base64.OutputStream(buf), deflater);
363
			}
364
			else {
365
				out = new Base64.OutputStream(buf);
366
			}
367
 
368
			for(int i = 0; i < stack.size()-suffix; i++) {
369
				String s = (String)stack.get(i);
370
				if(i > 0) out.write(URL_DELIMITER);
371
				out.write(s.getBytes(URL_CHARSET));
372
			}
373
			out.close();
374
		}
375
 
376
		return buf.toString(OUT_CHARSET);
377
	}
378
 
379
	/**
380
	 * @param stack List(String)
381
	 */
382
	protected void decode(List stack, String path, boolean zip)
383
		throws IOException
384
	{
385
		stack.clear();
386
		if(path == null || path.equals("")) return;
387
 
388
		ByteArrayOutputStream dec = new ByteArrayOutputStream(stack.size()*INIT_STREAM_SIZE_COEF);
389
		Object lock = (zip ? (Object)BackPath.class : this);
390
 
391
		synchronized(lock) {
392
			ByteArrayInputStream enc = new ByteArrayInputStream(path.getBytes(OUT_CHARSET));
393
 
394
			InputStream in;
395
			if(zip) {
396
				inflater.reset();
397
				in = new InflaterInputStream(new Base64.InputStream(enc), inflater);
398
			}
399
			else {
400
				in = new Base64.InputStream(enc);
401
			}
402
 
403
			byte[] tmp = new byte[BUFFER_SIZE];
404
			int    l;
405
			while((l = in.read(tmp, 0, tmp.length)) > 0) {
406
				dec.write(tmp, 0, l);
407
			}
408
		}
409
 
410
		try {
411
			byte[] buf = dec.toByteArray();
412
			for(int start = 0, end = 0; end <= buf.length; end++) {
413
				if(end == buf.length || buf[end] == URL_DELIMITER) {
414
					stack.add(new String(buf, start, end-start, URL_CHARSET));
415
					start = end+1;
416
				}
417
			}
418
		}
419
		catch(Exception ex) {  // if some mistake in stack, then ignore it completely
420
			stack.clear();
421
			log.info("Cannot parse stack", ex);
422
		}
423
	}
424
 
425
	protected void resetCachedStrings()
426
	{
427
		forwardParams        = null;
428
		forwardParamsFormed  = false;
429
		currentParams        = null;
430
		currentParamsFormed  = false;
1026 dev 431
		currentUrl           = null;
432
		currentUrlFormed     = false;
956 dev 433
		backwardParams       = null;
434
		backwardParamsFormed = false;
435
		backwardUrl          = null;
436
		backwardUrlFormed    = false;
437
	}
438
}