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 javax.faces.context.FacesContext;
21  import org.apache.shale.remoting.Mapping;
22  import org.apache.shale.remoting.Mappings;
23  import org.apache.shale.remoting.Mechanism;
24  import org.apache.shale.remoting.Processor;
25  
26  /***
27   * <p>Default implementation of {@link Mapping}.  This implementation recognizes
28   * patterns similar to URL mappings in the Servlet Specification:</p>
29   * <ul>
30   * <li><em>/foo/*</em> - prefix matching</li>
31   * <li><em>*.foo</em> - extension matching</li>
32   * </ul>
33   * <p>If a view identifier matches, the corresponding resource identifier is
34   * calculated by stripping the non-wildcard part of the view identifier
35   * (<code>/foo</code> or <code>.foo</code> for the examples above) and
36   * returning the remainder.</p>
37   */
38  public class MappingImpl implements Mapping {
39  
40  
41      // ------------------------------------------------------------ Constructors
42  
43  
44      /***
45       * <p>Construct an unconfigured instance.</p>
46       */
47      public MappingImpl() {
48          this(null, null, null);
49      }
50  
51  
52      /***
53       * <p>Construct a fully configured instance.</p>
54       *
55       * @param mechanism {@link Mechanism} used to produce response for this mapping
56       * @param pattern URL matching pattern for this mapping
57       * @param processor Processor instance for this mapping
58       */
59      public MappingImpl(Mechanism mechanism, String pattern, Processor processor) {
60          setMechanism(mechanism);
61          setPattern(pattern);
62          setProcessor(processor);
63      }
64  
65  
66      // ------------------------------------------------------ Instance Variables
67  
68  
69      /***
70       * <p>The {@link Mappings} instance that owns this mapping.</p>
71       */
72      private Mappings mappings = null;
73  
74  
75      /***
76       * <p>The non-wildcard part of the pattern for this mapping.</p>
77       */
78      private String match = null;
79  
80  
81      /***
82       * <p>The {@link Mechanism} used to produce response for this mapping.</p>
83       */
84      private Mechanism mechanism = null;
85  
86  
87      /***
88       * <p>The URL matching pattern for this mapping.</p>
89       */
90      private String pattern = null;
91  
92  
93      /***
94       * <p>Flag indicating we are doing prefix matching, versus extension
95       * mapping.</p>
96       */
97      private boolean prefix = true;
98  
99  
100     /***
101      * <p>The <code>Processor</code> instance for this mapping.</p>
102      */
103     private Processor processor = null;
104 
105 
106     // --------------------------------------------------------- Mapping Methods
107 
108 
109     /*** {@inheritDoc} */
110     public Mappings getMappings() {
111         return this.mappings;
112     }
113 
114 
115     /*** {@inheritDoc} */
116     public void setMappings(Mappings mappings) {
117         this.mappings = mappings;
118     }
119 
120 
121     /*** {@inheritDoc} */
122     public Mechanism getMechanism() {
123         return this.mechanism;
124     }
125 
126 
127     /*** {@inheritDoc} */
128     public void setMechanism(Mechanism mechanism) {
129         this.mechanism = mechanism;
130     }
131 
132 
133     /*** {@inheritDoc} */
134     public String getPattern() {
135         return this.pattern;
136     }
137 
138 
139     /*** {@inheritDoc} */
140     public void setPattern(String pattern) {
141 
142         if (pattern == null) {
143             this.match = null;
144             this.pattern = null;
145             return;
146         } else if (pattern.endsWith("/*")) {
147             if (!pattern.startsWith("/")) {
148                 throw new IllegalArgumentException(pattern);
149             }
150             this.match = pattern.substring(0, pattern.length() - 1);
151             this.pattern = pattern;
152             this.prefix = true;
153         } else if (pattern.startsWith("*.")) {
154             int period = pattern.lastIndexOf('.');
155             if (period != 1) {
156                 throw new IllegalArgumentException(pattern);
157             }
158             this.match = pattern.substring(1);
159             this.pattern = pattern;
160             this.prefix = false;
161         } else {
162             throw new IllegalArgumentException(pattern);
163         }
164 
165     }
166 
167 
168     /*** {@inheritDoc} */
169     public Processor getProcessor() {
170         return this.processor;
171     }
172 
173 
174     /*** {@inheritDoc} */
175     public void setProcessor(Processor processor) {
176         this.processor = processor;
177     }
178 
179 
180     /*** {@inheritDoc} */
181     public String mapResourceId(FacesContext context, String resourceId) {
182 
183         // Acquire the servlet mapping to be used for FacesServlet (if any --
184         // there will not be such a mapping in a portlet environment)
185         String[] patterns = getMappings().getPatterns();
186         String pattern = null;
187         if ((patterns != null) && (patterns.length > 0)) {
188             pattern = patterns[getMappings().getPatternIndex()];
189         }
190 
191         // Configure the entire URL we will return
192         StringBuffer sb = new StringBuffer();
193         if (pattern != null) {
194             sb.append(context.getExternalContext().getRequestContextPath());
195         }
196         if ((pattern != null) && (pattern.endsWith("/*"))) { // FacesServlet is prefix mapped
197             sb.append(pattern.substring(0, pattern.length() - 2));
198         }
199         if (getPattern().endsWith("*")) { // Processor is prefix mapped
200             sb.append(getPattern().substring(0, getPattern().length() - 2));
201         }
202         sb.append(resourceId);
203         if (getPattern().startsWith("*.")) { // Processor is extension mapped
204             sb.append(getPattern().substring(1));
205         }
206         if ((pattern != null) && (pattern.startsWith("*."))) { // FacesServlet is extension mapped
207             sb.append(pattern.substring(1));
208         }
209 
210         // Return the completed URL
211         if (pattern == null) {
212             // In a portlet environment, let the server map our "view identifier"
213             // to something that will be processed through the JSF lifecycle
214             return context.getApplication().getViewHandler().getActionURL(context, sb.toString());
215         } else {
216             // In a web application, our "view identifier" has already been
217             // mapped exactly the way we need it
218             return sb.toString();
219         }
220 
221     }
222 
223 
224     /*** {@inheritDoc} */
225     public String mapViewId(FacesContext context) {
226 
227         // Extract the view identifier we will be using to match against
228         String viewId = viewId(context);
229         if (viewId == null) {
230             return null;
231         }
232 
233         // Perform prefix or extension matching as requested
234         if (prefix) {
235             if (viewId.startsWith(match) && !viewId.equals(match)) {
236                 return viewId.substring(match.length() - 1);
237             } else {
238                 return null;
239             }
240         } else {
241             if (viewId.endsWith(match) && !viewId.equals(match)) {
242                 return viewId.substring(0, viewId.length() - match.length());
243             } else {
244                 return null;
245             }
246         }
247 
248     }
249 
250 
251     // ---------------------------------------------------------- Object Methods
252 
253 
254     /***
255      * <p>Return the hash code for this object.</p>
256      */
257     public int hashCode() {
258         if (this.pattern == null) {
259             return 0;
260         } else {
261             return this.pattern.hashCode();
262         }
263     }
264 
265 
266     /***
267      * <p>Two {@link Mapping}s are equal if they have the same pattern.</p>
268      *
269      * @param object Object to which we are tested for equality
270      */
271     public boolean equals(Object object) {
272         if ((object == null) || !(object instanceof Mapping)) {
273             return false;
274         }
275         if (this.pattern == null) {
276             return ((Mapping) object).getPattern() == null;
277         } else {
278             return this.pattern.equals(((Mapping) object).getPattern());
279         }
280     }
281 
282 
283     // --------------------------------------------------------- Private Methods
284 
285 
286     /***
287      * <p>Extract and return the view identifier for this request, after
288      * stripping any replacement suffix if <code>FacesServlet</code> is
289      * being extension mapped.</p>
290      *
291      * @param context <code>FacesContext</code> for the current request
292      */
293     private String viewId(FacesContext context) {
294 
295         // Get the raw view identifier
296         String viewId = context.getViewRoot().getViewId();
297 
298         // If the view identifier ends with the configured (or default)
299         // replacement suffix, just strip it and return
300         String extension = mappings.getExtension();
301         if ((extension != null) && (viewId.endsWith(extension))) {
302             return viewId.substring(0, viewId.length() - extension.length());
303         }
304 
305         // The JSF RI (version 1.1) has a bug where it does *not* replace
306         // the incoming extension during Restore View phase, as is required
307         // by Section 2.2.1 of the JSF Specification.  As a result, the view
308         // identifier immediately after Restore View completes will be something
309         // like "/index.faces" instead of "/index.jsp".  To work around this
310         // bug, walk through the URL patterns to which FacesServlet is mapped.
311         // If we detect an extension matching pattern that is found on our
312         // current view identifier, strip that and return as well.
313         String[] patterns = mappings.getPatterns();
314         if ((patterns == null) || (patterns.length < 1)) {
315             return viewId;
316         }
317         for (int i = 0; i < patterns.length; i++) {
318             if (!patterns[i].startsWith("*.")) {
319                 continue;
320             }
321             String match = patterns[i].substring(1);
322             if (viewId.endsWith(match)) {
323                 return viewId.substring(0, viewId.length() - match.length());
324             }
325         }
326 
327         // No matches, so just return what we have
328         return viewId;
329 
330     }
331 
332 }