001// Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 002// The Molly framework is freely distributable under the terms of an 003// MIT-style license. For details, see the molly pages web site at: 004// http://www.mollypages.org/. Use, modify, have fun ! 005 006package fc.web.page; 007 008import java.io.*; 009import java.util.*; 010import javax.servlet.*; 011import javax.servlet.http.*; 012 013import fc.io.*; 014import fc.web.servlet.*; 015 016/** 017The page servlet. Handles web requests that are mapped to a molly server 018page (typically via a <code>*.mp</code> extension). 019<p> 020All uncaught exceptions in the rendered page are wrapped in a 021<tt>ServletException</tt> and thrown up to the servlet container. These are 022typically handled by the container by either showing the full stack trace 023to the user or using a error page configured in the containers 024<tt>web.xml</tt> file. This latter approach is recommended for production 025use. An 026<i>example</i> is shown below. 027 <blockquote> 028 <xmp> 029 <!-- in web.xml --> 030 <error-page> 031 <exception-type>java.lang.Exception</exception-type> 032 <location>/errors/error.mp</location> 033 </error-page> 034 </xmp> 035 </blockquote> 036<p> 037However, if some partial response has already been sent to the browser and 038an exception occurs (later in java code on that same page), then the 039error page (if configured in web.xml) cannot typically be displayed. 040<p> 041For that scenario, this servlet accepts an <i>optional</i> 042<b><tt>error_page</tt></b> initialization parameter. This parameter, if 043present, should contain a <i>webapp</i>-relative path to an error page that 044will be <i>included</i> in the rendered page if there is an exception 045<i>after</i> the response has been committed. This error page is then 046<i>included</i> in the response sent to the browser. 047<p> 048The following attributes are available in the error page 049<pre> 050javax.servlet.error.status_code 051javax.servlet.error.exception 052javax.servlet.error.request_uri 053javax.servlet.error.servlet_name 054</pre> 055<p> 056This servlet also accepts an <i>optional</i> <b><tt>404_page</tt></b> 057parameter. This parameter, if present, should contain a <i>web document 058root</i>-relative path to a 404 or not found page. This page is different 059than the <i>error_page</i> because it signifies a badly typed URL request 060for a page that does not exist. (for example, 061<tt>http://somehost/badpage.mp</tt>). This parameter should be the same as 062the 404 error code parameter in <tt>web.xml</tt>. 063For example: 064 <blockquote> 065 <xmp> 066 <!-- in web.xml --> 067 <error-page> 068 <error-code>404</error-code> 069 <location>/errors/not_found.html</location> 070 </error-page> 071 </xmp> 072 </blockquote> 073<p> 074 075@author hursh jain 076*/ 077public class PageServlet extends FCBaseServlet 078{ 079private static final boolean dbg = false; 080 081PageMgr pagemgr; 082 083//error page to include if the response has already been committed 084//and an exception occurs. 085String error_page; 086String page_404; 087 088public void init(ServletConfig conf) throws ServletException 089 { 090 super.init(conf); 091 try { 092 ServletContext context = conf.getServletContext(); 093 094 synchronized (this) 095 { 096 String docrootstr = context.getRealPath("/"); 097 /* 098 docroot will be: 099 context = "", path = <wwwroot> 100 context = foo, path = <wwwroot>/foo 101 */ 102 if (docrootstr == null) 103 throw new UnavailableException("docroot == null, so I ask you, what can I do ?"); 104 105 File docroot = new File(docrootstr); 106 if (! docroot.exists()) 107 throw new UnavailableException("docroot " + docroot + " does not exist...so I ask you, what can I do ?"); 108 109 File scratchroot = null; 110 if (pagemgr == null) 111 { 112 scratchroot = new File(docroot + File.separator + 113 "WEB-INF" + File.separator + "molly_tmp"); 114 115 //mkdirs for /a/b/c.java makes THREE dirs: 'a', 'b' and 'c.java' 116 //but that's not an issue here. 117 if (! scratchroot.exists()) 118 scratchroot.mkdirs(); 119 120 pagemgr = new PageMgr(this, docroot, scratchroot, log); 121 } 122 123 error_page = WebUtil.getParam(this, "error_page", null); 124 page_404 = WebUtil.getParam(this, "404_page", null); 125 126 log.info("PageServlet init() finished"); 127 log.info("Document root=", docroot); 128 log.info("Scratch root=", scratchroot); 129 log.info("Error page=", error_page); 130 log.info("404 page=", page_404); 131 } 132 } 133 catch (Exception e) { 134 throw new ServletException(e); 135 } 136 } 137 138protected void service(HttpServletRequest req, HttpServletResponse res) 139throws ServletException, java.io.IOException 140 { 141 //The servlet engine fills in the servletpath appropriately for 142 //extension mapped servlets, so for example, if we have mapped 143 //*.mp or *.jsp to PageServlet, then when PageServlet gets the 144 //request for /a/b/c.mp, the servletpath in the request object 145 //will be /a/b/c.mp 146 147 //tomcat(piece of shit) sends context=/foo, servletpath=bar.jsp 148 //for /foo/bar.jsp even for the *DEFAULT* (/) context. 149 //So we say: 150 // String path = req.getContextPath() + req.getServletPath(); 151 //looks like tomcat(piece of shit) may or may not have fixed the 152 //above bug. i just don't give a shit about tomcat anymore. 153 // 154 //The page path is relative from the context. The pagemgr was created 155 //with the appropriate docroot that includes the webapp context (if any). 156 //We just need to pass it the servlet path to get the page. 157 String path = req.getServletPath(); 158 159 if (dbg) log.bug("Enter request for path: ", path); 160 161 if (path == null) 162 throw new ServletException("Don't know how to handle this request, servletpath was null"); 163 164 try { 165 if (dbg) { 166 System.out.format("PageServlet: INCLUDE info: uri=%s, c_path=%s, s_path=%s, path_info=%s, q=%s", 167 (String) req.getAttribute("javax.servlet.include.req_uri"), 168 (String) req.getAttribute("javax.servlet.include.context_path"), 169 (String) req.getAttribute("javax.servlet.include.servlet_path"), 170 (String) req.getAttribute("javax.servlet.include.path_info"), 171 (String) req.getAttribute("javax.servlet.include.query_string")); 172 173 System.out.println(); 174 175 System.out.format("PageServlet: FORWARD info: uri=%s, c_path=%s, s_path=%s, path_info=%s, q=%s", 176 (String) req.getAttribute("javax.servlet.forward.req_uri"), 177 (String) req.getAttribute("javax.servlet.forward.context_path"), 178 (String) req.getAttribute("javax.servlet.forward.servlet_path"), 179 (String) req.getAttribute("javax.servlet.forward.path_info"), 180 (String) req.getAttribute("javax.servlet.forward.query_string")); 181 182 System.out.println(); 183 } 184 185 final String include_path = 186 (String) req.getAttribute("javax.servlet.include.servlet_path"); 187 188 if (include_path != null) { 189 //RequestDispatcher include ? 190 path = include_path; 191 } 192 else{ 193 /* 194 WE DONT DO THIS SINCE forward.* properties are for the ORIGINAL 195 REQUEST. the path is already properly set for forwards. 196 197 final String forward_path = 198 (String) req.getAttribute("javax.servlet.forward.servlet_path"); 199 //RequestDispatcher forward ? 200 if (forward_path != null) { 201 path = forward_path; 202 } 203 */ 204 } 205 206 if (dbg) log.bug("Asking PageMgr for the page: ", path); 207 Page page = pagemgr.getPage(path); 208 if (dbg) log.bug("PageMgr returned page: [", path, "]:", page); 209 210 if (page == null) 211 { 212 if (page_404 == null) { 213 throw new ServletException("Molly page:" + path + " not found."); 214 } 215 else{ 216 WebUtil.clientRedirect(req, res, page_404); 217 return; 218 } 219 } 220 221 page.render(req, res); 222 } 223 catch (Exception e) 224 { 225 //this helps us figure out bugs in the "error.mp" page itself (which 226 //otherwise hard to see, since error.mp -> parse error --> error.mp) 227 if (dbg) log.bug(IOUtil.throwableToString(e)); 228 229 //error occured after page partially shown to user 230 if (res.isCommitted() && error_page != null) 231 { 232 req.setAttribute("javax.servlet.error.status_code", 500); 233 req.setAttribute("javax.servlet.error.exception", e); 234 req.setAttribute("javax.servlet.error.request_uri", path); 235 req.setAttribute("javax.servlet.error.servlet_name", "Molly Page Servlet"); 236 237 RequestDispatcher rd = req.getRequestDispatcher(error_page); 238 rd.include(req, res); 239 return; 240 } 241 242 //page hasn't been shown to the user. The container will set all 243 //the javax.servlet.error.* properties and forward to the error 244 //page (if any) that is configured for the container. 245 ServletException se = new ServletException(e.toString()); 246 se.setStackTrace(e.getStackTrace()); 247 throw se; 248 } 249 } 250 251public String toString() { 252 return "Scrumptious and delicious Page Servlet ! (See: www.mollypages.org)"; 253 } 254 255public void destroy() 256 { 257 try { 258 pagemgr.destroy(); 259 pagemgr = null; 260 super.destroy(); 261 } 262 catch (Exception e) { 263 System.out.println(IOUtil.throwableToString(e)); 264 } 265 } 266 267}