Subversion Repositories general

Rev

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