View Javadoc

1   package net.sf.tacos.rest;
2   
3   import java.net.URL;
4   import java.net.MalformedURLException;
5   import java.io.File;
6   import java.io.IOException;
7   import java.util.Set;
8   import java.util.Map;
9   import javax.servlet.http.HttpServlet;
10  import javax.servlet.ServletContext;
11  
12  import org.apache.tapestry.INamespace;
13  import org.apache.tapestry.Tapestry;
14  import org.apache.tapestry.engine.ServiceEncoder;
15  import org.apache.tapestry.engine.ServiceEncoding;
16  import org.apache.tapestry.services.ServiceConstants;
17  import org.apache.tapestry.services.ApplicationInitializer;
18  import org.apache.commons.collections.FastHashMap;
19  
20  import org.scannotation.AnnotationDB;
21  
22  /**
23   * Rest-style encoder for pages.<p/> The urls generated and understood are of the form
24   * /view/pagename (so, you'll have to register a servlet mapping for /view in web.xml).
25   * <p/>
26   * If page classes have a {@link UriTemplate} annotation, upon initialization this service 
27   * will scan, gather and use those mappings for url generation (instead of the pagename).
28   */
29  public class RestPageEncoder implements ServiceEncoder, ApplicationInitializer {
30  
31      private String url;
32      private boolean caseSensitive;
33  
34      private Map<String, String> pathToPageMap;
35      private Map<String, String> pageToPathMap;
36  
37      public void encode(ServiceEncoding encoding) {
38          String service = encoding.getParameterValue(ServiceConstants.SERVICE);
39  
40          if (!service.equals(Tapestry.PAGE_SERVICE)) {
41              return;
42          }
43  
44          String pageName = encoding.getParameterValue(ServiceConstants.PAGE);
45  
46          // Only handle pages in the application namespace (not from a library).
47          if (pageName.indexOf(INamespace.SEPARATOR) >= 0) {
48              return;
49          }
50  
51          StringBuffer buffer = new StringBuffer(url);
52          buffer.append('/');
53          String pathOverride = pageToPathMap.get(pageName);
54          if (pathOverride != null) {
55              buffer.append(pathOverride);
56          } else {
57              buffer.append(pageName);
58          }
59  
60          encoding.setServletPath(buffer.toString());
61          encoding.setParameterValue(ServiceConstants.SERVICE, null);
62          encoding.setParameterValue(ServiceConstants.PAGE, null);
63      }
64  
65      public void decode(ServiceEncoding encoding) {
66          String servletPath = encoding.getServletPath();
67  
68          if (!servletPath.startsWith(url)) {
69              return;
70          }
71  
72          // handle only if there's path info
73          if (encoding.getPathInfo()==null) {
74              return;
75          }
76  
77          // Skip the slash and the dot.
78          String page = encoding.getPathInfo().substring(1);
79  
80          String pageOverride = caseSensitive ?
81                  pathToPageMap.get(page) : pathToPageMap.get(page.toLowerCase());
82          if (pageOverride != null) {
83              page = pageOverride;
84          }
85  
86          encoding.setParameterValue(ServiceConstants.SERVICE, Tapestry.PAGE_SERVICE);
87          encoding.setParameterValue(ServiceConstants.PAGE, page);
88      }
89  
90      public void setUrl(String url) {
91          this.url = url;
92      }
93  
94      public void setCaseSensitive(boolean caseSensitive) {
95          this.caseSensitive = caseSensitive;
96      }
97  
98      public void initialize(HttpServlet servlet) {
99          FastHashMap pageToPath = new FastHashMap();
100         FastHashMap pathToPage = new FastHashMap();
101 
102         URL webInfClasses = findWebInfClassesPath(servlet.getServletContext());
103 
104         scan(pageToPath, pathToPage, webInfClasses);
105 
106         storeMaps(pageToPath, pathToPage);
107     }
108 
109     private void scan(FastHashMap pageToPath, FastHashMap pathToPage, URL url) {
110         if (url == null)
111             return;
112 
113         AnnotationDB db = new AnnotationDB();
114         db.addIgnoredPackages("org");
115         try {
116             db.scanArchives(url);
117         } catch (IOException e) {
118             return;
119         }
120 
121         Set<String> simpleClasses = db.getAnnotationIndex().get(UriTemplate.class.getName());
122         if (simpleClasses == null)
123             return;
124 
125         for (String simpleClass : simpleClasses) {
126             UriTemplate uriTemplate;
127             try {
128                 uriTemplate = Class.forName(simpleClass).getAnnotation(UriTemplate.class);
129             } catch (ClassNotFoundException e) {
130                 continue;
131             }
132 
133             String path = uriTemplate.value();
134             if (path.startsWith("/")) {
135                 path = path.substring(1);
136             }
137             if (!caseSensitive) {
138                 path = path.toLowerCase();
139             }
140 
141             String page = toPage(simpleClass);
142             pathToPage.put(path, page);
143             pageToPath.put(page, path);
144 
145             // now process aliases
146             for (String aliasPath : uriTemplate.alias()) {
147                 pathToPage.put(aliasPath, page);
148             }
149 
150         }
151     }
152 
153     @SuppressWarnings("unchecked")
154     private void storeMaps(FastHashMap pageToPath, FastHashMap pathToPage) {
155         pageToPath.setFast(true);
156         pathToPage.setFast(true);
157         pageToPathMap = pageToPath;
158         pathToPageMap = pathToPage;
159     }
160 
161     /**
162      * Given the classname, returns the tapestry page name.
163      * TODO: assumes parent package is pages, better use tap. configuration of page packages
164      * @param className
165      * @return
166      */
167     private String toPage(String className) {
168         int pos = className.indexOf(".pages.");
169         return className.substring(pos + 7).replace('.', '/');
170     }
171 
172     private URL findWebInfClassesPath(ServletContext servletContext) {
173         String path = servletContext.getRealPath("/WEB-INF/classes");
174         if (path == null) return null;
175         File fp = new File(path);
176         if (!fp.exists()) return null;
177         try {
178             return fp.toURI().toURL();
179         }
180         catch (MalformedURLException e) {
181             throw new RuntimeException(e);
182         }
183     }
184 }