File Source: WeblogRequestMapper.java
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. The ASF licenses this file to You
4 * under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License. For additional information regarding
15 * copyright in this work, please see the NOTICE file in the top level
16 * directory of this distribution.
17 */
18
19 package org.apache.roller.weblogger.ui.rendering;
20
21 import java.io.IOException;
22 import java.util.HashSet;
23 import java.util.Set;
24 import javax.servlet.RequestDispatcher;
25 import javax.servlet.ServletException;
26 import javax.servlet.http.HttpServletRequest;
27 import javax.servlet.http.HttpServletResponse;
28
29 import org.apache.commons.lang.StringUtils;
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.roller.weblogger.config.WebloggerConfig;
33 import org.apache.roller.weblogger.business.WebloggerFactory;
34 import org.apache.roller.weblogger.business.UserManager;
35 import org.apache.roller.weblogger.pojos.Weblog;
36
37
38 /**
39 * Handles rendering requests for Roller pages/feeds by routing to the appropriate Servlet.
40 *
41 * This request mapper is used to map all weblog specific urls of the form
42 * /<weblog handle>/* to the appropriate servlet for handling the actual
43 * request.
44 *
45 * TODO: we should try and make this class easier to extend and build upon
46 */
47 public class WeblogRequestMapper implements RequestMapper {
48
/*
P/P * Method: org.apache.roller.weblogger.ui.rendering.WeblogRequestMapper__static_init
*
* Postconditions:
* init'ed(log)
*/
49 private static Log log = LogFactory.getLog(WeblogRequestMapper.class);
50
51 private static final String PAGE_SERVLET = "/roller-ui/rendering/page";
52 private static final String FEED_SERVLET = "/roller-ui/rendering/feed";
53 private static final String RESOURCE_SERVLET = "/roller-ui/rendering/resources";
54 private static final String SEARCH_SERVLET = "/roller-ui/rendering/search";
55 private static final String RSD_SERVLET = "/roller-ui/rendering/rsd";
56
57 private static final String COMMENT_SERVLET = "/roller-ui/rendering/comment";
58 private static final String TRACKBACK_SERVLET = "/roller-ui/rendering/trackback";
59
60
61 // url patterns that are not allowed to be considered weblog handles
62 Set restricted = null;
63
64
/*
P/P * Method: void org.apache.roller.weblogger.ui.rendering.WeblogRequestMapper()
*
* Postconditions:
* this.restricted == &new HashSet(WeblogRequestMapper#1)
* new HashSet(WeblogRequestMapper#1) num objects == 1
*
* Test Vectors:
* java.lang.String:length(...)@72: {0}, {1..232-1}
* java.lang.String:length(...)@82: {0}, {1..232-1}
* org.apache.roller.weblogger.config.WebloggerConfig:getProperty(...)@70: Addr_Set{null}, Inverse{null}
* org.apache.roller.weblogger.config.WebloggerConfig:getProperty(...)@80: Addr_Set{null}, Inverse{null}
*/
65 public WeblogRequestMapper() {
66
67 this.restricted = new HashSet();
68
69 // build roller restricted list
70 String restrictList =
71 WebloggerConfig.getProperty("rendering.weblogMapper.rollerProtectedUrls");
72 if(restrictList != null && restrictList.trim().length() > 0) {
73 String[] restrict = restrictList.split(",");
+ 74 for(int i=0; i < restrict.length; i++) {
+ 75 this.restricted.add(restrict[i]);
76 }
77 }
78
79 // add user restricted list
80 restrictList =
81 WebloggerConfig.getProperty("rendering.weblogMapper.userProtectedUrls");
82 if(restrictList != null && restrictList.trim().length() > 0) {
83 String[] restrict = restrictList.split(",");
+ 84 for(int i=0; i < restrict.length; i++) {
+ 85 this.restricted.add(restrict[i]);
86 }
87 }
88 }
89
90
91 public boolean handleRequest(HttpServletRequest request, HttpServletResponse response)
92 throws ServletException, IOException {
93
94 // kinda silly, but we need to keep track of whether or not the url had
95 // a trailing slash so that we can act accordingly
/*
P/P * Method: bool handleRequest(HttpServletRequest, HttpServletResponse)
*
* Preconditions:
* log != null
* request != null
* this.restricted != null
* (soft) response != null
*
* Presumptions:
* java.lang.String:indexOf(...)@125 <= 232-2
* javax.servlet.http.HttpServletRequest:getContextPath(...)@112 != null
* javax.servlet.http.HttpServletRequest:getRequestDispatcher(...)@224 != null
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* java.lang.String:endsWith(...)@118: {0}, {1}
* java.lang.String:indexOf(...)@123: {-1}, {-231..-2, 0..232-1}
* java.lang.String:length(...)@109: {0,1}, {2..232-1}
* java.util.Set:contains(...)@134: {1}, {0}
* javax.servlet.http.HttpServletRequest:getContextPath(...)@111: Addr_Set{null}, Inverse{null}
* javax.servlet.http.HttpServletRequest:getQueryString(...)@189: Addr_Set{null}, Inverse{null}
* javax.servlet.http.HttpServletRequest:getRequestURI(...)@106: Addr_Set{null}, Inverse{null}
*/
96 boolean trailingSlash = false;
97
98 String weblogHandle = null;
99 String weblogLocale = null;
100 String weblogRequestContext = null;
101 String weblogRequestData = null;
102
103 log.debug("evaluating ["+request.getRequestURI()+"]");
104
105 // figure out potential weblog handle
106 String servlet = request.getRequestURI();
107 String pathInfo = null;
108
109 if(servlet != null && servlet.trim().length() > 1) {
110
111 if(request.getContextPath() != null)
112 servlet = servlet.substring(request.getContextPath().length());
113
114 // strip off the leading slash
115 servlet = servlet.substring(1);
116
117 // strip off trailing slash if needed
118 if(servlet.endsWith("/")) {
119 servlet = servlet.substring(0, servlet.length() - 1);
120 trailingSlash = true;
121 }
122
123 if(servlet.indexOf("/") != -1) {
124 weblogHandle = servlet.substring(0, servlet.indexOf("/"));
125 pathInfo = servlet.substring(servlet.indexOf("/")+1);
126 } else {
127 weblogHandle = servlet;
128 }
129 }
130
131 log.debug("potential weblog handle = "+weblogHandle);
132
133 // check if it's a valid weblog handle
134 if(restricted.contains(weblogHandle) || !this.isWeblog(weblogHandle)) {
135 log.debug("SKIPPED "+weblogHandle);
136 return false;
137 }
138
139 log.debug("WEBLOG_URL "+request.getServletPath());
140
141 // parse the rest of the url and build forward url
142 if(pathInfo != null) {
143
144 // parse the next portion of the url
145 // we expect [locale/]<context>/<extra>/<info>
146 String[] urlPath = pathInfo.split("/", 3);
147
148 // if we have a locale, deal with it
+ 149 if(this.isLocale(urlPath[0])) {
+ 150 weblogLocale = urlPath[0];
151
152 // no extra path info specified
153 if(urlPath.length == 2) {
154 weblogRequestContext = urlPath[1];
155 weblogRequestData = null;
156
157 // request contains extra path info
158 } else if(urlPath.length == 3) {
159 weblogRequestContext = urlPath[1];
160 weblogRequestData = urlPath[2];
161 }
162
163 // otherwise locale is empty
164 } else {
165 weblogLocale = null;
+ 166 weblogRequestContext = urlPath[0];
167
168 // last part of request is extra path info
+ 169 if(urlPath.length == 2) {
+ 170 weblogRequestData = urlPath[1];
171
172 // if we didn't have a locale then we have split too much
173 // so we reassemble the last 2 path elements together
+ 174 } else if(urlPath.length == 3) {
+ 175 weblogRequestData = urlPath[1] + "/" + urlPath[2];
176 }
177 }
178
179 }
180
181 // special handling for trailing slash issue
182 // we need this because by http standards the urls /foo and /foo/ are
183 // supposed to be considered different, so we must enforce that
+ 184 if(weblogRequestContext == null && !trailingSlash) {
185 // this means someone referred to a weblog index page with the
186 // shortest form of url /<weblog> or /<weblog>/<locale> and we need
187 // to do a redirect to /<weblog>/ or /<weblog>/<locale>/
188 String redirectUrl = request.getRequestURI() + "/";
189 if(request.getQueryString() != null) {
190 redirectUrl += "?"+request.getQueryString();
191 }
192
193 response.sendRedirect(redirectUrl);
194 return true;
195
+ 196 } else if(weblogRequestContext != null &&
197 "tags".equals(weblogRequestContext)) {
198 // tags section can have an index page at /<weblog>/tags/ and
199 // a tags query at /<weblog>/tags/tag1+tag2, buth that's it
200 if((weblogRequestData == null && !trailingSlash) ||
201 (weblogRequestData != null && trailingSlash)) {
202 response.sendError(HttpServletResponse.SC_NOT_FOUND);
203 return true;
204 }
+ 205 } else if(weblogRequestContext != null && trailingSlash) {
206 // this means that someone has accessed a weblog url and included
207 // a trailing slash, like /<weblog>/entry/<anchor>/ which is not
208 // supported, so we need to offer up a 404 Not Found
209 response.sendError(HttpServletResponse.SC_NOT_FOUND);
210 return true;
211 }
212
213 // calculate forward url
214 String forwardUrl = calculateForwardUrl(request, weblogHandle, weblogLocale,
215 weblogRequestContext, weblogRequestData);
216
217 // if we don't have a forward url then the request was invalid somehow
218 if(forwardUrl == null) {
219 return false;
220 }
221
222 // dispatch to forward url
223 log.debug("forwarding to "+forwardUrl);
224 RequestDispatcher dispatch = request.getRequestDispatcher(forwardUrl);
225 dispatch.forward(request, response);
226
227 // we dealt with this request ourselves, so return "true"
228 return true;
229 }
230
231
232 /**
233 * Convenience method for caculating the servlet forward url given a set
234 * of information to make the decision with.
235 *
236 * handle is always assumed valid, all other params may be null.
237 */
238 private String calculateForwardUrl(HttpServletRequest request,
239 String handle, String locale,
240 String context, String data) {
241
/*
P/P * Method: String calculateForwardUrl(HttpServletRequest, String, String, String, String)
*
* Preconditions:
* log != null
* request != null
*
* Postconditions:
* init'ed(java.lang.StringBuffer:toString(...)._tainted)
* return_value in Addr_Set{null,&java.lang.StringBuffer:toString(...)}
*
* Test Vectors:
* context: Inverse{null}, Addr_Set{null}
* data: Addr_Set{null}, Inverse{null}
* locale: Addr_Set{null}, Inverse{null}
* java.lang.String:equals(...)@247: {0}, {1}
* java.lang.String:equals(...)@249: {0}, {1}
* java.lang.String:equals(...)@303: {1}, {0}
* java.lang.String:equals(...)@303: {0}, {1}
* java.lang.String:equals(...)@322: {0}, {1}
* java.lang.String:equals(...)@337: {0}, {1}
* java.lang.String:equals(...)@348: {0}, {1}
* ...
*/
242 log.debug(handle+","+locale+","+context+","+data);
243
244 StringBuffer forwardUrl = new StringBuffer();
245
246 // POST urls, like comment and trackback servlets
247 if("POST".equals(request.getMethod())) {
248 // posting to permalink, this means comment or trackback
+ 249 if(context.equals("entry")) {
250 // trackback requests are required to have an "excerpt" param
251 if(request.getParameter("excerpt") != null) {
252
253 forwardUrl.append(TRACKBACK_SERVLET);
254 forwardUrl.append("/");
255 forwardUrl.append(handle);
256 if(locale != null) {
257 forwardUrl.append("/");
258 forwardUrl.append(locale);
259 }
260 forwardUrl.append("/");
261 forwardUrl.append(context);
262 if(data != null) {
263 forwardUrl.append("/");
264 forwardUrl.append(data);
265 }
266
267 // comment requests are required to have a "content" param
268 } else if(request.getParameter("content") != null) {
269
270 forwardUrl.append(COMMENT_SERVLET);
271 forwardUrl.append("/");
272 forwardUrl.append(handle);
273 if(locale != null) {
274 forwardUrl.append("/");
275 forwardUrl.append(locale);
276 }
277 forwardUrl.append("/");
278 forwardUrl.append(context);
279 if(data != null) {
280 forwardUrl.append("/");
281 forwardUrl.append(data);
282 }
283 }
284
285 } else {
286 // someone posting data where they aren't supposed to
287 return null;
288 }
289
290 } else {
291 // no context means weblog homepage
292 if(context == null) {
293
294 forwardUrl.append(PAGE_SERVLET);
295 forwardUrl.append("/");
296 forwardUrl.append(handle);
297 if(locale != null) {
298 forwardUrl.append("/");
299 forwardUrl.append(locale);
300 }
301
302 // requests handled by PageServlet
303 } else if(context.equals("page") || context.equals("entry") ||
304 context.equals("date") || context.equals("category")
305 || context.equals("tags")) {
306
307 forwardUrl.append(PAGE_SERVLET);
308 forwardUrl.append("/");
309 forwardUrl.append(handle);
310 if(locale != null) {
311 forwardUrl.append("/");
312 forwardUrl.append(locale);
313 }
314 forwardUrl.append("/");
315 forwardUrl.append(context);
316 if(data != null) {
317 forwardUrl.append("/");
318 forwardUrl.append(data);
319 }
320
321 // requests handled by FeedServlet
322 } else if(context.equals("feed")) {
323
324 forwardUrl.append(FEED_SERVLET);
325 forwardUrl.append("/");
326 forwardUrl.append(handle);
327 if(locale != null) {
328 forwardUrl.append("/");
329 forwardUrl.append(locale);
330 }
331 if(data != null) {
332 forwardUrl.append("/");
333 forwardUrl.append(data);
334 }
335
336 // requests handled by ResourceServlet
337 } else if(context.equals("resource")) {
338
339 forwardUrl.append(RESOURCE_SERVLET);
340 forwardUrl.append("/");
341 forwardUrl.append(handle);
342 if(data != null) {
343 forwardUrl.append("/");
344 forwardUrl.append(data);
345 }
346
347 // requests handled by SearchServlet
348 } else if(context.equals("search")) {
349
350 forwardUrl.append(SEARCH_SERVLET);
351 forwardUrl.append("/");
352 forwardUrl.append(handle);
353
354 // requests handled by RSDServlet
355 } else if(context.equals("rsd")) {
356
357 forwardUrl.append(RSD_SERVLET);
358 forwardUrl.append("/");
359 forwardUrl.append(handle);
360
361 // unsupported url
362 } else {
363 return null;
364 }
365 }
366
367 log.debug("FORWARD_URL "+forwardUrl.toString());
368
369 return forwardUrl.toString();
370 }
371
372
373 /**
374 * convenience method which determines if the given string is a valid
375 * weblog handle.
376 *
377 * TODO 3.0: some kind of caching
378 */
379 private boolean isWeblog(String potentialHandle) {
380
/*
P/P * Method: bool isWeblog(String)
*
* Preconditions:
* log != null
*
* Presumptions:
* org.apache.roller.weblogger.business.Weblogger:getUserManager(...)@386 != null
* org.apache.roller.weblogger.business.WebloggerFactory:getWeblogger(...)@386 != null
*
* Postconditions:
* init'ed(return_value)
*
* Test Vectors:
* org.apache.roller.weblogger.business.UserManager:getWebsiteByHandle(...)@387: Addr_Set{null}, Inverse{null}
*/
381 log.debug("checking weblog handle "+potentialHandle);
382
383 boolean isWeblog = false;
384
385 try {
386 UserManager mgr = WebloggerFactory.getWeblogger().getUserManager();
387 Weblog weblog = mgr.getWebsiteByHandle(potentialHandle);
388
389 if(weblog != null) {
390 isWeblog = true;
391 }
392 } catch(Exception ex) {
393 // doesn't really matter to us why it's not a valid website
394 }
395
396 return isWeblog;
397 }
398
399
400 /**
401 * Convenience method which determines if the given string is a valid
402 * locale string.
403 */
404 private boolean isLocale(String potentialLocale) {
405
/*
P/P * Method: bool isLocale(String)
*
* Postconditions:
* return_value == 0
*
* Test Vectors:
* potentialLocale: Addr_Set{null}, Inverse{null}
* java.lang.String:length(...)@409: {2}, {0,1, 3..232-1}
* java.lang.String:length(...)@409: {0..4, 6..232-1}, {5}
*/
406 boolean isLocale = false;
407
408 // we only support 2 or 5 character locale strings, so check that first
409 if(potentialLocale != null &&
410 (potentialLocale.length() == 2 || potentialLocale.length() == 5)) {
411
412 // now make sure that the format is proper ... e.g. "en_US"
413 // we are not going to be picky about capitalization
414 String[] langCountry = potentialLocale.split("_");
+ 415 if(langCountry.length == 1 &&
416 langCountry[0] != null && langCountry[0].length() == 2) {
417 isLocale = true;
418
+ 419 } else if(langCountry.length == 2 &&
420 langCountry[0] != null && langCountry[0].length() == 2 &&
421 langCountry[1] != null && langCountry[1].length() == 2) {
422
423 isLocale = true;
424 }
425 }
426
427 return isLocale;
428 }
429
430 }
SofCheck Inspector Build Version : 2.18479
| WeblogRequestMapper.java |
2009-Jan-02 14:25:26 |
| WeblogRequestMapper.class |
2009-Sep-04 03:12:44 |