2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

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  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      private String excludes = null;
45  
46  
47      /***
48       * <p>Array of regular expression patterns for the excludes list.</p>
49       */
50      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      private String includes = null;
58  
59  
60      /***
61       * <p>Array of regular expression patterns for the includes list.</p>
62       */
63      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      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          this.excludes = excludes;
86          this.excludesPatterns = precompile(excludes);
87      }
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      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         this.includes = includes;
107         this.includesPatterns = precompile(includes);
108     }
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         if (matches(resourceId, excludesPatterns, false)) {
125             return false;
126         }
127 
128         // Check for a match on the included list
129         if (matches(resourceId, includesPatterns, true)) {
130             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         if ((patterns == null) || (patterns.length == 0)) {
163             return unrestricted;
164         }
165 
166         // Compare each pattern in turn for a match
167         for (int i = 0; i < patterns.length; i++) {
168             if (patterns[i].startsWith("*")) {
169                 if (expr.endsWith(patterns[i].substring(1))) {
170                     return true;
171                 }
172             } else if (patterns[i].endsWith("*")) {
173                 if (expr.startsWith(patterns[i].substring(0, patterns[i].length() - 1))) {
174                     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         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         if (expr == null) {
207             return new String[0];
208         }
209 
210         // Set up to parse the specified expression
211         String pattern = null;
212         StreamTokenizer st =
213           new StreamTokenizer(new StringReader(expr));
214         st.eolIsSignificant(false);
215         st.lowerCaseMode(false);
216         st.slashSlashComments(false);
217         st.slashStarComments(false);
218         st.wordChars(0x00, 0xff);
219         st.quoteChar('\'');
220         st.quoteChar('"');
221         st.whitespaceChars(0, ' ');
222         st.whitespaceChars(',', ',');
223         List list = new ArrayList();
224         int type = 0;
225 
226         // Parse and validate each included pattern
227         while (true) {
228 
229             // Parse the next pattern
230             try {
231                 type = st.nextToken();
232             } catch (IOException e) {
233                 ; // Can not happen
234             }
235             if (type == StreamTokenizer.TT_EOF) {
236                 break;
237             } else if (type == StreamTokenizer.TT_NUMBER) {
238                 pattern = "" + st.nval;
239             } else if (type == StreamTokenizer.TT_WORD) {
240                 pattern = st.sval.trim();
241             } else {
242                 throw new IllegalArgumentException(expr);
243             }
244 
245             // Validate this pattern
246             if (pattern.length() < 1) {
247                 throw new IllegalArgumentException(pattern);
248             }
249             if (pattern.startsWith("/")) {
250                 if (!pattern.endsWith("/*")) {
251                     throw new IllegalArgumentException(pattern);
252                 }
253             } else if (pattern.startsWith("*.")) {
254                 if (pattern.substring(2).indexOf('.') > 0) {
255                     throw new IllegalArgumentException(pattern);
256                 }
257             } else {
258                 throw new IllegalArgumentException(pattern);
259             }
260 
261             // Add this pattern to our list
262             list.add(pattern);
263 
264         }
265 
266         // Return the precompiled patterns as an array
267         return (String[]) list.toArray(new String[list.size()]);
268 
269     }
270 
271 
272 }