View Javadoc

1   /**
2    * Copyright (C) 2005-2009 Alfresco Software Limited.
3    *
4    * This file is part of the Spring Surf Extension project.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.springframework.extensions.webscripts;
20  
21  import java.util.Comparator;
22  import java.util.Map;
23  import java.util.TreeMap;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  
28  
29  /**
30   * JSR-311 (Jax-RS) URI Index
31   *   
32   * @author davidc
33   */
34  public class JaxRSUriIndex implements UriIndex
35  {
36      // Logger
37      private static final Log logger = LogFactory.getLog(JaxRSUriIndex.class);
38      
39      // map of web scripts by url
40      private Map<IndexEntry, IndexEntry> index = new TreeMap<IndexEntry, IndexEntry>(COMPARATOR);
41      
42      
43      /* (non-Javadoc)
44       * @see org.alfresco.web.scripts.UriIndex#clear()
45       */
46      public void clear()
47      {
48          index.clear();
49      }
50  
51      /* (non-Javadoc)
52       * @see org.alfresco.web.scripts.UriIndex#getSize()
53       */
54      public int getSize()
55      {
56          return index.size();
57      }
58  
59      /* (non-Javadoc)
60       * @see org.alfresco.web.scripts.UriIndex#findWebScript(java.lang.String, java.lang.String)
61       */
62      public Match findWebScript(String method, String uri)
63      {
64          IndexEntry pathMatch = null;
65          Map<String, String> varsMatch = null;
66          Match scriptMatch = null;
67          String match = uri;
68          String matchNoExt = uri;
69          int extIdx = uri.indexOf('.');
70          if (extIdx != -1)
71          {
72              // format extension is only valid as the last URL element
73              if (uri.lastIndexOf('/') < extIdx)
74              {
75                  matchNoExt = uri.substring(0, extIdx);
76              }
77          }
78          method = method.toUpperCase();
79          
80          // locate full match - on URI and METHOD
81          for (IndexEntry entry : index.keySet())
82          {
83              String test = entry.getIncludeExtension() ? match : matchNoExt;
84              Map<String, String> vars = entry.getTemplate().match(test);
85              if (vars != null)
86              {
87                  pathMatch = entry;
88                  varsMatch = vars;
89                  if (entry.getMethod().equals(method))
90                  {
91                      scriptMatch = new Match(entry.getTemplate().getTemplate(), vars, entry.getStaticTemplate(), entry.getScript()); 
92                      break;
93                  }
94              }
95          }
96          
97          // locate URI match
98          if (scriptMatch == null && pathMatch != null)
99          {
100             scriptMatch = new Match(pathMatch.getTemplate().getTemplate(), varsMatch, pathMatch.getStaticTemplate());
101         }
102         
103         return scriptMatch;
104     }
105 
106     /* (non-Javadoc)
107      * @see org.alfresco.web.scripts.UriIndex#registerUri(org.alfresco.web.scripts.WebScript, java.lang.String)
108      */
109     public void registerUri(WebScript script, String uri)
110     {
111         Description desc = script.getDescription();
112         boolean extension = true;
113 
114         // trim uri parameters
115         int queryArgIdx = uri.indexOf('?');
116         if (queryArgIdx != -1)
117         {
118             uri = uri.substring(0, queryArgIdx);
119         }
120         
121         // trim extension, only if script distinguishes response format via the extension
122         if (desc.getFormatStyle() != Description.FormatStyle.argument)
123         {
124             int extIdx = uri.lastIndexOf(".");
125             if (extIdx != -1)
126             {
127                 uri = uri.substring(0, extIdx);
128             }
129             extension = false;
130         }
131         
132         // index service ensuring no other service has already claimed the url
133         IndexEntry entry = new IndexEntry(desc.getMethod(), new UriTemplate(uri), extension, script);
134         if (index.containsKey(entry))
135         {
136             IndexEntry existingEntry = index.get(entry);
137             WebScript existingService = existingEntry.getScript();
138             if (!existingService.getDescription().getId().equals(desc.getId()))
139             {
140                 String msg = "Web Script document " + desc.getDescPath() + " is attempting to define the url '" + entry + "' already defined by " + existingService.getDescription().getDescPath();
141                 throw new WebScriptException(msg);
142             }
143         }
144         else
145         {
146             index.put(entry, entry);
147             if (logger.isTraceEnabled())
148                 logger.trace("Indexed URI '" + uri + "' as '" + entry.getTemplate() + "'");
149         }
150     }
151 
152     /**
153      * Comparator for ordering Uri Templates index entries according to JSR-311
154      */
155     static final Comparator<IndexEntry> COMPARATOR = new Comparator<IndexEntry>()
156     {
157         public int compare(IndexEntry o1, IndexEntry o2)
158         {
159             if (o1 == null && o2 == null)
160             {
161                 return 0;
162             }
163             if (o1 == null)
164             {
165                 return 1;
166             }
167             if (o2 == null)
168             {
169                 return -1;
170             }
171 
172             // primary key: order by number of static characters in URI template
173             int i = o2.getTemplate().getStaticCharCount() - o1.getTemplate().getStaticCharCount();
174             if (i != 0)
175             {
176                 return i;
177             }
178 
179             // secondary key: order by number of template vars
180             i = o2.getTemplate().getVariableNames().length - o1.getTemplate().getVariableNames().length;
181             if (i != 0)
182             {
183                 return i;
184             }
185 
186             // order by uri template regular expression
187             i = o2.getTemplate().getRegex().pattern().compareTo(o1.getTemplate().getRegex().pattern());
188             if (i != 0)
189             {
190                 return i;
191             }
192             
193             // implementation specific: order by http method
194             return o2.method.compareTo(o1.method);
195         }
196     };
197 
198     /**
199      * URI Index Entry
200      * 
201      * @author davidc
202      */
203     static class IndexEntry
204     {
205         private String method;
206         private UriTemplate template;
207         private WebScript script;
208         private boolean includeExtension;
209         private String staticTemplate;
210         private final String key;
211 
212         /**
213          * Construct
214          * 
215          * @param method   http method
216          * @param template  uri template
217          * @param includeExtension  include uri extension in index
218          * @param script  associated web script
219          */
220         IndexEntry(String method, UriTemplate template, boolean includeExtension, WebScript script)
221         {
222             this.method = method.toUpperCase();
223             this.template = template;
224             this.includeExtension = includeExtension;
225             this.script = script;
226             this.key = template.getRegex() + ":" + this.method;
227             int firstTokenIdx = template.getTemplate().indexOf('{');
228             this.staticTemplate = (firstTokenIdx == -1) ? template.getTemplate() : template.getTemplate().substring(0, firstTokenIdx);
229         }
230 
231         /**
232          * @return  http method
233          */
234         public String getMethod()
235         {
236             return method;
237         }
238 
239         /**
240          * @return  uri template
241          */
242         public UriTemplate getTemplate()
243         {
244             return template;
245         }
246         
247         /**
248          * @return  static prefix of uri template
249          */
250         public String getStaticTemplate()
251         {
252             return staticTemplate;
253         }
254 
255         /**
256          * @return  includes uri extension in index
257          */
258         public boolean getIncludeExtension()
259         {
260             return includeExtension;
261         }
262 
263         /**
264          * @return  associated web script
265          */
266         public WebScript getScript()
267         {
268             return script;
269         }
270         
271         @Override
272         public final String toString()
273         {
274             return key;
275         }
276         
277         @Override
278         public boolean equals(Object obj)
279         {
280             if (!(obj instanceof IndexEntry))
281             {
282                 return false;
283             }
284             return key.equals(((IndexEntry)obj).key);
285         }
286 
287         @Override
288         public int hashCode()
289         {
290             return key.hashCode();
291         }
292     }
293 }