2009/05/20 - Apache Shale has been retired.
For more information, please explore the Attic.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.shale.application;
19
20 import java.io.IOException;
21 import java.io.StreamTokenizer;
22 import java.io.StringReader;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.regex.Pattern;
26
27 import javax.servlet.http.HttpServletResponse;
28
29 import org.apache.commons.chain.Command;
30 import org.apache.commons.chain.Context;
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.shale.application.faces.ShaleWebContext;
34
35 /***
36 * <p>Convenience base class for <code>Command</code> implementations that
37 * perform regular expression matching against a set of zero or more
38 * patterns. The default <code>Command</code> implementation will perform
39 * the following algorithm.</p>
40 * <ul>
41 * <li>Retrieve the value to be compared by calling <code>value()</code>.</li>
42 * <li>If the specified value is <code>null</code>, call <code>reject()</code>
43 * and return <code>true</code> to indicate that request procesing is
44 * complete.</li>
45 * <li>If there are any include patterns, and the value matches one of
46 * these patterns, call <code>accept()</code> and return
47 * <code>false</code> to indicate request processing should continue.</li>
48 * <li>If there are any exclude patterns, and the value matches one of
49 * these patterns, call <code>reject()</code> and return
50 * <code>true</code> to indicate that request processing is complete.</li>
51 * <li>If there are any include patterns, and the value does not match one of
52 * these patterns, call <code>reject()</code> and return
53 * <code>true</code> to indicate that request processing is complete.</li>
54 * <li>Call <code>accept()</code> and return <code>false</code> to indicate
55 * that request processing should continue.</li>
56 * </ul>
57 *
58 * <p><strong>USAGE NOTE:</strong> - See the class JavaDocs for
59 * <code>java.util.regex.Pattern</code> for the valid syntax for regular
60 * expression patterns supported by this command.</p>
61 *
62 * <p><strong>USAGE NOTE:</strong> - Commands based on this class will only
63 * be effective if used before the regular filter chain is processed. In
64 * other words, you should invoke it as part of a <code>preprocess</code>
65 * chain in the <code>shale</code> catalog.</p>
66 *
67 * $Id: AbstractRegExpFilter.java 464373 2006-10-16 04:21:54Z rahul $
68 */
69
70 public abstract class AbstractRegExpFilter implements Command {
71
72
73
74
75
76
77 /***
78 * <p>Log instance for this class.</p>
79 */
80 private static final Log log = LogFactory.getLog(AbstractRegExpFilter.class);
81
82
83
84
85
86 /***
87 * <p>Comma-delimited regular expression patterns to exclude remote host
88 * names that match.</p>
89 */
90 private String excludes = null;
91
92
93 /***
94 * <p>Array of regular expression patterns for the excludes list.</p>
95 */
96 private Pattern excludesPatterns[] = new Pattern[0];
97
98
99 /***
100 * <p>Comma-delimited regular expression patterns to include remote host
101 * names that match.</p>
102 */
103 private String includes = null;
104
105
106 /***
107 * <p>Array of regular expression patterns for the includes list.</p>
108 */
109 private Pattern includesPatterns[] = new Pattern[0];
110
111 /***
112 * <p>Returns an array of regular expression patterns for the includes list.</p>
113 */
114 protected Pattern[] getIncludesPatterns() { return includesPatterns; }
115
116
117
118
119
120 /***
121 * <p>Return the comma-delimited regular expresson patterns to exclude
122 * remote host names that match, if any; otherwise, return
123 * <code>null</code>.</p>
124 */
125 public String getExcludes() { return this.excludes; }
126
127
128 /***
129 * <p>Set the comma-delimited regular expression patterns to exclude
130 * remote host names that match, if any; or <code>null</code> for no
131 * restrictions.</p>
132 *
133 * @param excludes New exclude pattern(s)
134 */
135 public void setExcludes(String excludes) {
136 this.excludes = excludes;
137 this.excludesPatterns = precompile(excludes);
138 }
139
140
141 /***
142 * <p>Return the comma-delimited regular expresson patterns to include
143 * remote host names that match, if any; otherwise, return
144 * <code>null</code>.</p>
145 */
146 public String getIncludes() { return this.includes; }
147
148
149 /***
150 * <p>Set the comma-delimited regular expression patterns to include
151 * remote host names that match, if any; or <code>null</code> for no
152 * restrictions.</p>
153 *
154 * @param includes New include pattern(s)
155 */
156 public void setIncludes(String includes) {
157 this.includes = includes;
158 this.includesPatterns = precompile(includes);
159 }
160
161
162
163
164
165 /***
166 * <p>Perform the matching algorithm described in our class Javadocs
167 * against the value returned by the <code>value()</code> method.</p>
168 *
169 * @param context <code>ShaleWebContext</code> for this request
170 *
171 * @exception Exception if an error occurs
172 */
173 public boolean execute(Context context) throws Exception {
174
175
176 ShaleWebContext webContext = (ShaleWebContext) context;
177 String value = value(webContext);
178 if (log.isDebugEnabled()) {
179 log.debug("execute(" + value + ")");
180 }
181 if (value == null) {
182 if (log.isTraceEnabled()) {
183 log.trace(" reject(null)");
184 }
185 reject(webContext);
186 return true;
187 }
188
189
190 if (matches(value, includesPatterns, true)) {
191 if (log.isTraceEnabled()) {
192 log.trace(" accept(include)");
193 }
194 accept(webContext);
195 return false;
196 }
197
198
199 if (matches(value, excludesPatterns, false)) {
200 if (log.isTraceEnabled()) {
201 log.trace(" reject(exclude)");
202 }
203 reject(webContext);
204 return true;
205 }
206
207
208
209 if ((includesPatterns != null) && (includesPatterns.length > 0)) {
210 if (log.isTraceEnabled()) {
211 log.trace(" reject(not include)");
212 }
213 reject(webContext);
214 return true;
215 }
216
217
218 if (log.isTraceEnabled()) {
219 log.trace(" accept(default)");
220 }
221 accept(webContext);
222 return false;
223
224 }
225
226
227
228
229
230 /***
231 * <p>Perform whatever processing is necessary to mark this request as
232 * being accepted. The default implementation does nothing.</p>
233 *
234 * @param context <code>Context</code> for the current request
235 *
236 * @exception Exception if an error occurs
237 */
238 protected void accept(ShaleWebContext context) throws Exception {
239
240
241
242 }
243
244
245 /***
246 * <p>Perform whatever processing is necessary to mark this request as
247 * being rejected. The default implementation returns a status code
248 * of <code>HttpServletResponse.SC_FORBIDDEN</code>.</p>
249 *
250 * @param context <code>Context</code> for the current request
251 *
252 * @exception Exception if an error occurs
253 */
254 protected void reject(ShaleWebContext context) throws Exception {
255
256 HttpServletResponse response = context.getResponse();
257 response.sendError(HttpServletResponse.SC_FORBIDDEN);
258
259 }
260
261
262 /***
263 * <p>Return the value, from the specified context, that should be used
264 * to match against the configured exclude and include patterns. This
265 * method must be implemented by a concrete subclass.</p>
266 *
267 * @param context <code>Context</code> for the current request
268 */
269 protected abstract String value(ShaleWebContext context);
270
271
272
273
274
275 /***
276 * <p>Match the specified expression against the specified precompiled
277 * patterns. If there are no patterns, return the specified unrestricted
278 * return value; otherwise, return <code>true</code> if the expression
279 * matches one of the patterns, or <code>false</code> otherwise.</p>
280 *
281 * @param expr Expression to be tested
282 * @param patterns Array of <code>Pattern</code> to be tested against
283 * @param unrestricted Result to be returned if there are no matches
284 */
285 protected boolean matches(String expr, Pattern patterns[],
286 boolean unrestricted) {
287
288
289 if ((patterns == null) || (patterns.length == 0)) {
290 return unrestricted;
291 }
292
293
294 for (int i = 0; i < patterns.length; i++) {
295 if (patterns[i].matcher(expr).matches()) {
296 return true;
297 }
298 }
299
300
301 return false;
302
303 }
304
305
306 /***
307 * <p>Parse the specified string of comma-delimited (and optionally quoted,
308 * if an embedded comma is required) regular expressions into an array
309 * of precompiled <code>Pattern</code> instances that represent these
310 * expressons.</p>
311 *
312 * @param expr Comma-delimited regular expressions
313 */
314 private Pattern[] precompile(String expr) {
315
316 if (expr == null) {
317 return new Pattern[0];
318 }
319
320
321 StreamTokenizer st =
322 new StreamTokenizer(new StringReader(expr));
323 st.eolIsSignificant(false);
324 st.lowerCaseMode(false);
325 st.slashSlashComments(false);
326 st.slashStarComments(false);
327 st.wordChars(0x00, 0xff);
328 st.quoteChar('\'');
329 st.quoteChar('"');
330 st.whitespaceChars(0, ' ');
331 st.whitespaceChars(',', ',');
332 List list = new ArrayList();
333 int type = 0;
334
335
336 while (true) {
337 try {
338 type = st.nextToken();
339 } catch (IOException e) {
340 ;
341 }
342 if (type == StreamTokenizer.TT_EOF) {
343 break;
344 } else if (type == StreamTokenizer.TT_NUMBER) {
345 list.add(Pattern.compile("" + st.nval));
346 } else if (type == StreamTokenizer.TT_WORD) {
347 list.add(Pattern.compile(st.sval.trim()));
348 } else {
349 throw new IllegalArgumentException(expr);
350 }
351 }
352
353
354 return (Pattern[]) list.toArray(new Pattern[list.size()]);
355
356 }
357
358
359 }