Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
FilteringProcessor |
|
| 5.857142857142857;5.857 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
3 | * contributor license agreements. See the NOTICE file distributed with | |
4 | * this work for additional information regarding copyright ownership. | |
5 | * The ASF licenses this file to you under the Apache License, Version 2.0 | |
6 | * (the "License"); you may not use this file except in compliance with | |
7 | * the License. You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software | |
12 | * distributed under the License is distributed on an "AS IS" BASIS, | |
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | * See the License for the specific language governing permissions and | |
15 | * limitations under the License. | |
16 | */ | |
17 | ||
18 | package org.apache.shale.remoting.impl; | |
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 org.apache.shale.remoting.Processor; | |
26 | ||
27 | /** | |
28 | * <p>Abstract base class for {@link Processor} implementations that filter | |
29 | * requests based on matching the resource identifier against a set of | |
30 | * <code>includes</code> and <code>excludes</code> regular expressions.</p> | |
31 | * | |
32 | * @since 1.0.4 | |
33 | */ | |
34 | 30 | public abstract class FilteringProcessor implements Processor { |
35 | ||
36 | ||
37 | // ------------------------------------------------------ Instance Variables | |
38 | ||
39 | ||
40 | /** | |
41 | * <p>Comma-delimited regular expression patterns to exclude remote host | |
42 | * names that match.</p> | |
43 | */ | |
44 | 30 | private String excludes = null; |
45 | ||
46 | ||
47 | /** | |
48 | * <p>Array of regular expression patterns for the excludes list.</p> | |
49 | */ | |
50 | 30 | private String excludesPatterns[] = new String[0]; |
51 | ||
52 | ||
53 | /** | |
54 | * <p>Comma-delimited regular expression patterns to include remote host | |
55 | * names that match.</p> | |
56 | */ | |
57 | 30 | private String includes = null; |
58 | ||
59 | ||
60 | /** | |
61 | * <p>Array of regular expression patterns for the includes list.</p> | |
62 | */ | |
63 | 30 | private String includesPatterns[] = new String[0]; |
64 | ||
65 | ||
66 | // -------------------------------------------------------------- Properties | |
67 | ||
68 | ||
69 | /** | |
70 | * <p>Return the comma-delimited regular expresson patterns to exclude | |
71 | * remote host names that match, if any; otherwise, return | |
72 | * <code>null</code>.</p> | |
73 | */ | |
74 | 4 | public String getExcludes() { return this.excludes; } |
75 | ||
76 | ||
77 | /** | |
78 | * <p>Set the comma-delimited regular expression patterns to exclude | |
79 | * remote host names that match, if any; or <code>null</code> for no | |
80 | * restrictions.</p> | |
81 | * | |
82 | * @param excludes New exclude pattern(s) | |
83 | */ | |
84 | public void setExcludes(String excludes) { | |
85 | 36 | this.excludes = excludes; |
86 | 36 | this.excludesPatterns = precompile(excludes); |
87 | 35 | } |
88 | ||
89 | ||
90 | /** | |
91 | * <p>Return the comma-delimited regular expresson patterns to include | |
92 | * remote host names that match, if any; otherwise, return | |
93 | * <code>null</code>.</p> | |
94 | */ | |
95 | 4 | public String getIncludes() { return this.includes; } |
96 | ||
97 | ||
98 | /** | |
99 | * <p>Set the comma-delimited regular expression patterns to include | |
100 | * remote host names that match, if any; or <code>null</code> for no | |
101 | * restrictions.</p> | |
102 | * | |
103 | * @param includes New include pattern(s) | |
104 | */ | |
105 | public void setIncludes(String includes) { | |
106 | 34 | this.includes = includes; |
107 | 34 | this.includesPatterns = precompile(includes); |
108 | 33 | } |
109 | ||
110 | ||
111 | // ------------------------------------------------------- Protected Methods | |
112 | ||
113 | ||
114 | /** | |
115 | * <p>Return <code>true</code> if we should accept a request for the | |
116 | * specified resource identifier, based upon our configured includes | |
117 | * and excludes patterns (if any).</p> | |
118 | * | |
119 | * @param resourceId Resource identifier to validate | |
120 | */ | |
121 | protected boolean accept(String resourceId) { | |
122 | ||
123 | // Check for a match on the excluded list | |
124 | 18 | if (matches(resourceId, excludesPatterns, false)) { |
125 | 5 | return false; |
126 | } | |
127 | ||
128 | // Check for a match on the included list | |
129 | 13 | if (matches(resourceId, includesPatterns, true)) { |
130 | 13 | return true; |
131 | } | |
132 | ||
133 | // If there was at least one include pattern, | |
134 | // unconditionally reject this request | |
135 | if ((includesPatterns != null) && (includesPatterns.length > 0)) { | |
136 | return false; | |
137 | } | |
138 | ||
139 | // Unconditionally accept this request | |
140 | return true; | |
141 | ||
142 | } | |
143 | ||
144 | ||
145 | // --------------------------------------------------------- Private Methods | |
146 | ||
147 | ||
148 | /** | |
149 | * <p>Match the specified expression against the specified precompiled | |
150 | * patterns. If there are no patterns, return the specified unrestricted | |
151 | * return value; otherwise, return <code>true</code> if the expression | |
152 | * matches one of the patterns, or <code>false</code> otherwise.</p> | |
153 | * | |
154 | * @param expr Expression to be tested | |
155 | * @param patterns Array of <code>Pattern</code> to be tested against | |
156 | * @param unrestricted Result to be returned if there are no matches | |
157 | */ | |
158 | private boolean matches(String expr, String patterns[], | |
159 | boolean unrestricted) { | |
160 | ||
161 | // Check for the unrestricted case | |
162 | 31 | if ((patterns == null) || (patterns.length == 0)) { |
163 | 6 | return unrestricted; |
164 | } | |
165 | ||
166 | // Compare each pattern in turn for a match | |
167 | 221 | for (int i = 0; i < patterns.length; i++) { |
168 | 208 | if (patterns[i].startsWith("*")) { |
169 | 58 | if (expr.endsWith(patterns[i].substring(1))) { |
170 | 8 | return true; |
171 | } | |
172 | 150 | } else if (patterns[i].endsWith("*")) { |
173 | 150 | if (expr.startsWith(patterns[i].substring(0, patterns[i].length() - 1))) { |
174 | 4 | return true; |
175 | } | |
176 | } else { | |
177 | if (patterns[i].equals(expr)) { | |
178 | return true; | |
179 | } | |
180 | } | |
181 | } | |
182 | ||
183 | // No match found, so return false | |
184 | 13 | return false; |
185 | ||
186 | } | |
187 | ||
188 | ||
189 | /** | |
190 | * <p>Parse the specified string of comma-delimited URL pattern | |
191 | * matching expressions into an array of patterns that can be processed | |
192 | * at runtime more quickly. Valid patterns are the same as those | |
193 | * supported for matching a request URI to a Processor instance:</p> | |
194 | * <ul> | |
195 | * <li>Must not be null or zero-length string</li> | |
196 | * <li>EITHER must start with "/" and end with "/*"</li> | |
197 | * <li>OR must start with "*." and not have any other period</li> | |
198 | * </ul> | |
199 | * | |
200 | * @param expr Comma-delimited URL pattern matching expressions | |
201 | * | |
202 | * @exception IllegalArgumentException if an invalid pattern is encountered | |
203 | */ | |
204 | private String[] precompile(String expr) { | |
205 | ||
206 | 70 | if (expr == null) { |
207 | 14 | return new String[0]; |
208 | } | |
209 | ||
210 | // Set up to parse the specified expression | |
211 | 56 | String pattern = null; |
212 | 56 | StreamTokenizer st = |
213 | new StreamTokenizer(new StringReader(expr)); | |
214 | 56 | st.eolIsSignificant(false); |
215 | 56 | st.lowerCaseMode(false); |
216 | 56 | st.slashSlashComments(false); |
217 | 56 | st.slashStarComments(false); |
218 | 56 | st.wordChars(0x00, 0xff); |
219 | 56 | st.quoteChar('\''); |
220 | 56 | st.quoteChar('"'); |
221 | 56 | st.whitespaceChars(0, ' '); |
222 | 56 | st.whitespaceChars(',', ','); |
223 | 56 | List list = new ArrayList(); |
224 | 56 | int type = 0; |
225 | ||
226 | // Parse and validate each included pattern | |
227 | while (true) { | |
228 | ||
229 | // Parse the next pattern | |
230 | try { | |
231 | 566 | type = st.nextToken(); |
232 | } catch (IOException e) { | |
233 | ; // Can not happen | |
234 | 566 | } |
235 | 566 | if (type == StreamTokenizer.TT_EOF) { |
236 | 54 | break; |
237 | 512 | } else if (type == StreamTokenizer.TT_NUMBER) { |
238 | pattern = "" + st.nval; | |
239 | } else if (type == StreamTokenizer.TT_WORD) { | |
240 | 512 | pattern = st.sval.trim(); |
241 | 512 | } else { |
242 | throw new IllegalArgumentException(expr); | |
243 | } | |
244 | ||
245 | // Validate this pattern | |
246 | 512 | if (pattern.length() < 1) { |
247 | throw new IllegalArgumentException(pattern); | |
248 | } | |
249 | 512 | if (pattern.startsWith("/")) { |
250 | 294 | if (!pattern.endsWith("/*")) { |
251 | 1 | throw new IllegalArgumentException(pattern); |
252 | } | |
253 | 218 | } else if (pattern.startsWith("*.")) { |
254 | 218 | if (pattern.substring(2).indexOf('.') > 0) { |
255 | 1 | throw new IllegalArgumentException(pattern); |
256 | } | |
257 | } else { | |
258 | throw new IllegalArgumentException(pattern); | |
259 | } | |
260 | ||
261 | // Add this pattern to our list | |
262 | 510 | list.add(pattern); |
263 | ||
264 | 510 | } |
265 | ||
266 | // Return the precompiled patterns as an array | |
267 | 54 | return (String[]) list.toArray(new String[list.size()]); |
268 | ||
269 | } | |
270 | ||
271 | ||
272 | } |