1 52 53 package freemarker.ext.servlet; 54 55 import java.io.File ; 56 import java.io.FileNotFoundException ; 57 import java.io.IOException ; 58 import java.text.SimpleDateFormat ; 59 import java.util.Calendar ; 60 import java.util.Enumeration ; 61 import java.util.GregorianCalendar ; 62 63 import javax.servlet.ServletContext ; 64 import javax.servlet.ServletException ; 65 import javax.servlet.http.HttpServlet ; 66 import javax.servlet.http.HttpServletRequest ; 67 import javax.servlet.http.HttpServletResponse ; 68 import javax.servlet.http.HttpSession ; 69 70 import freemarker.cache.WebappTemplateLoader; 71 import freemarker.core.Configurable; 72 import freemarker.ext.jsp.TaglibFactory; 73 import freemarker.log.Logger; 74 import freemarker.template.Configuration; 75 import freemarker.template.ObjectWrapper; 76 import freemarker.template.Template; 77 import freemarker.template.TemplateException; 78 import freemarker.template.TemplateExceptionHandler; 79 import freemarker.template.TemplateModel; 80 import freemarker.template.TemplateModelException; 81 import freemarker.template.utility.StringUtil; 82 import java.util.Locale ; 83 84 162 163 public class FreemarkerServlet extends HttpServlet 164 { 165 private static final Logger logger = Logger.getLogger("freemarker.servlet"); 166 167 public static final long serialVersionUID = -2440216393145762479L; 168 169 private static final String INITPARAM_TEMPLATE_PATH = "TemplatePath"; 170 private static final String INITPARAM_NOCACHE = "NoCache"; 171 private static final String INITPARAM_CONTENT_TYPE = "ContentType"; 172 private static final String DEFAULT_CONTENT_TYPE = "text/html"; 173 private static final String INITPARAM_DEBUG = "Debug"; 174 175 private static final String DEPR_INITPARAM_TEMPLATE_DELAY = "TemplateDelay"; 176 private static final String DEPR_INITPARAM_ENCODING = "DefaultEncoding"; 177 private static final String DEPR_INITPARAM_OBJECT_WRAPPER = "ObjectWrapper"; 178 private static final String DEPR_INITPARAM_WRAPPER_SIMPLE = "simple"; 179 private static final String DEPR_INITPARAM_WRAPPER_BEANS = "beans"; 180 private static final String DEPR_INITPARAM_WRAPPER_JYTHON = "jython"; 181 private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER = "TemplateExceptionHandler"; 182 private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_RETHROW = "rethrow"; 183 private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_DEBUG = "debug"; 184 private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_HTML_DEBUG = "htmlDebug"; 185 private static final String DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_IGNORE = "ignore"; 186 private static final String DEPR_INITPARAM_DEBUG = "debug"; 187 188 public static final String KEY_REQUEST = "Request"; 189 public static final String KEY_REQUEST_PARAMETERS = "RequestParameters"; 190 public static final String KEY_SESSION = "Session"; 191 public static final String KEY_APPLICATION = "Application"; 192 public static final String KEY_JSP_TAGLIBS = "JspTaglibs"; 193 194 private static final String ATTR_REQUEST_MODEL = ".freemarker.Request"; 197 private static final String ATTR_REQUEST_PARAMETERS_MODEL = 198 ".freemarker.RequestParameters"; 199 private static final String ATTR_SESSION_MODEL = ".freemarker.Session"; 200 private static final String ATTR_APPLICATION_MODEL = 201 ".freemarker.Application"; 202 private static final String ATTR_JSP_TAGLIBS_MODEL = 203 ".freemarker.JspTaglibs"; 204 205 private static final String EXPIRATION_DATE; 206 207 static { 208 GregorianCalendar expiration = new GregorianCalendar (); 210 expiration.roll(Calendar.YEAR, -1); 211 SimpleDateFormat httpDate = 212 new SimpleDateFormat ( 213 "EEE, dd MMM yyyy HH:mm:ss z", 214 java.util.Locale.US); 215 EXPIRATION_DATE = httpDate.format(expiration.getTime()); 216 } 217 218 private String templatePath; 219 private boolean nocache; 220 protected boolean debug; 221 private Configuration config; 222 private ObjectWrapper wrapper; 223 private String contentType; 224 private boolean noCharsetInContentType; 225 226 public void init() throws ServletException { 227 try { 228 config = createConfiguration(); 229 230 config.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER); 232 contentType = DEFAULT_CONTENT_TYPE; 233 234 wrapper = createObjectWrapper(); 236 if (logger.isDebugEnabled()) { 237 logger.debug("Using object wrapper of class " + wrapper.getClass().getName()); 238 } 239 config.setObjectWrapper(wrapper); 240 241 templatePath = getInitParameter(INITPARAM_TEMPLATE_PATH); 243 if (templatePath == null) 244 templatePath = "class://"; 245 if (templatePath.startsWith("class://")) { 246 config.setClassForTemplateLoading( 248 getClass(), 249 templatePath.substring(7)); 250 } else { 251 if (templatePath.startsWith("file://")) { 252 templatePath = templatePath.substring(7); 253 config.setDirectoryForTemplateLoading(new File (templatePath)); 254 } else { 255 config.setTemplateLoader(new WebappTemplateLoader(this.getServletContext(), templatePath)); 256 } 257 } 258 259 Enumeration initpnames = getServletConfig().getInitParameterNames(); 261 while (initpnames.hasMoreElements()) { 262 String name = (String ) initpnames.nextElement(); 263 String value = getInitParameter(name); 264 265 if (name == null) { 266 throw new ServletException ( 267 "init-param without param-name. " 268 + "Maybe the web.xml is not well-formed?"); 269 } 270 if (value == null) { 271 throw new ServletException ( 272 "init-param without param-value. " 273 + "Maybe the web.xml is not well-formed?"); 274 } 275 276 if (name.equals(DEPR_INITPARAM_OBJECT_WRAPPER) 277 || name.equals(Configurable.OBJECT_WRAPPER_KEY) 278 || name.equals(INITPARAM_TEMPLATE_PATH)) { 279 } else if (name.equals(DEPR_INITPARAM_ENCODING)) { if (getInitParameter(Configuration.DEFAULT_ENCODING_KEY) != null) { 282 throw new ServletException ( 283 "Conflicting init-params: " 284 + Configuration.DEFAULT_ENCODING_KEY + " and " 285 + DEPR_INITPARAM_ENCODING); 286 } 287 config.setDefaultEncoding(value); 288 } else if (name.equals(DEPR_INITPARAM_TEMPLATE_DELAY)) { if (getInitParameter(Configuration.TEMPLATE_UPDATE_DELAY_KEY) != null) { 290 throw new ServletException ( 291 "Conflicting init-params: " 292 + Configuration.TEMPLATE_UPDATE_DELAY_KEY + " and " 293 + DEPR_INITPARAM_TEMPLATE_DELAY); 294 } 295 try { 296 config.setTemplateUpdateDelay(Integer.parseInt(value)); 297 } catch (NumberFormatException e) { 298 } 300 } else if (name.equals(DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER)) { if (getInitParameter(Configurable.TEMPLATE_EXCEPTION_HANDLER_KEY) != null) { 302 throw new ServletException ( 303 "Conflicting init-params: " 304 + Configurable.TEMPLATE_EXCEPTION_HANDLER_KEY + " and " 305 + DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER); 306 } 307 308 if (DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_RETHROW.equals(value)) { 309 config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); 310 } else if (DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_DEBUG.equals(value)) { 311 config.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER); 312 } else if (DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_HTML_DEBUG.equals(value)) { 313 config.setTemplateExceptionHandler(TemplateExceptionHandler.HTML_DEBUG_HANDLER); 314 } else if (DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER_IGNORE.equals(value)) { 315 config.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER); 316 } else { 317 throw new ServletException ( 318 "Invalid value for servlet init-param " 319 + DEPR_INITPARAM_TEMPLATE_EXCEPTION_HANDLER + ": " + value); 320 } 321 } else if (name.equals(INITPARAM_NOCACHE)) { 322 nocache = StringUtil.getYesNo(value); 323 } else if (name.equals(DEPR_INITPARAM_DEBUG)) { if (getInitParameter(INITPARAM_DEBUG) != null) { 325 throw new ServletException ( 326 "Conflicting init-params: " 327 + INITPARAM_DEBUG + " and " 328 + DEPR_INITPARAM_DEBUG); 329 } 330 debug = StringUtil.getYesNo(value); 331 } else if (name.equals(INITPARAM_DEBUG)) { 332 debug = StringUtil.getYesNo(value); 333 } else if (name.equals(INITPARAM_CONTENT_TYPE)) { 334 contentType = value; 335 } else { 336 config.setSetting(name, value); 337 } 338 } 340 noCharsetInContentType = true; 341 int i = contentType.toLowerCase().indexOf("charset="); 342 if (i != -1) { 343 char c = ' '; 344 i--; 345 while (i >= 0) { 346 c = contentType.charAt(i); 347 if (!Character.isWhitespace(c)) break; 348 i--; 349 } 350 if (i == -1 || c == ';') { 351 noCharsetInContentType = false; 352 } 353 } 354 } catch (ServletException e) { 355 throw e; 356 } catch (Exception e) { 357 throw new ServletException (e); 358 } 359 } 360 361 public void doGet(HttpServletRequest request, HttpServletResponse response) 362 throws ServletException , IOException 363 { 364 process(request, response); 365 } 366 367 public void doPost( 368 HttpServletRequest request, 369 HttpServletResponse response) 370 throws ServletException , IOException 371 { 372 process(request, response); 373 } 374 375 private void process( 376 HttpServletRequest request, 377 HttpServletResponse response) 378 throws ServletException , IOException 379 { 380 if (preprocessRequest(request, response)) { 382 return; 383 } 384 385 String path = requestUrlToTemplatePath(request); 386 387 if (debug) { 388 log("Requested template: " + path); 389 } 390 391 Template template = null; 392 try { 393 template = config.getTemplate( 394 path, 395 deduceLocale(path, request, response)); 396 } catch (FileNotFoundException e) { 397 response.sendError(HttpServletResponse.SC_NOT_FOUND); 398 return; 399 } 400 401 Object attrContentType = template.getCustomAttribute("content_type"); 402 if(attrContentType != null) { 403 response.setContentType(attrContentType.toString()); 404 } 405 else { 406 if (noCharsetInContentType) { 407 response.setContentType( 408 contentType + "; charset=" + template.getEncoding()); 409 } else { 410 response.setContentType(contentType); 411 } 412 } 413 414 setBrowserCachingPolicy(response); 416 417 ServletContext servletContext = getServletContext(); 418 try { 419 TemplateModel model = createModel(wrapper, servletContext, request, response); 420 421 if (preTemplateProcess(request, response, template, model)) { 423 try { 424 template.process(model, response.getWriter()); 426 } finally { 427 postTemplateProcess(request, response, template, model); 429 } 430 } 431 } catch (TemplateException te) { 432 if (config.getTemplateExceptionHandler() 433 .getClass().getName().indexOf("Debug") != -1) { 434 this.log("Error executing FreeMarker template", te); 435 } else { 436 throw new ServletException ("Error executing FreeMarker template", te); 437 } 438 } 439 } 440 441 448 protected Locale deduceLocale( 449 String templatePath, HttpServletRequest request, HttpServletResponse response) { 450 return config.getLocale(); 451 } 452 453 protected TemplateModel createModel(ObjectWrapper wrapper, 454 ServletContext servletContext, 455 HttpServletRequest request, 456 HttpServletResponse response) throws TemplateModelException { 457 try { 458 AllHttpScopesHashModel params = new AllHttpScopesHashModel(wrapper, servletContext, request); 459 460 ServletContextHashModel servletContextModel = 462 (ServletContextHashModel) servletContext.getAttribute( 463 ATTR_APPLICATION_MODEL); 464 if (servletContextModel == null) 465 { 466 servletContextModel = new ServletContextHashModel(this, wrapper); 467 servletContext.setAttribute( 468 ATTR_APPLICATION_MODEL, 469 servletContextModel); 470 TaglibFactory taglibs = new TaglibFactory(servletContext); 471 servletContext.setAttribute( 472 ATTR_JSP_TAGLIBS_MODEL, 473 taglibs); 474 initializeServletContext(request, response); 475 } 476 params.putUnlistedModel(KEY_APPLICATION, servletContextModel); 477 params.putUnlistedModel(KEY_JSP_TAGLIBS, (TemplateModel)servletContext.getAttribute(ATTR_JSP_TAGLIBS_MODEL)); 478 HttpSessionHashModel sessionModel; 480 HttpSession session = request.getSession(false); 481 if(session != null) { 482 sessionModel = (HttpSessionHashModel) session.getAttribute(ATTR_SESSION_MODEL); 483 if (sessionModel == null) { 484 sessionModel = new HttpSessionHashModel(session, wrapper); 485 session.setAttribute(ATTR_SESSION_MODEL, sessionModel); 486 initializeSession(request, response); 487 } 488 } 489 else { 490 sessionModel = new HttpSessionHashModel(this, request, response, wrapper); 491 } 492 params.putUnlistedModel(KEY_SESSION, sessionModel); 493 494 HttpRequestHashModel requestModel = 496 (HttpRequestHashModel) request.getAttribute(ATTR_REQUEST_MODEL); 497 if (requestModel == null || requestModel.getRequest() != request) 498 { 499 requestModel = new HttpRequestHashModel(request, response, wrapper); 500 request.setAttribute(ATTR_REQUEST_MODEL, requestModel); 501 request.setAttribute( 502 ATTR_REQUEST_PARAMETERS_MODEL, 503 createRequestParametersHashModel(request)); 504 } 505 params.putUnlistedModel(KEY_REQUEST, requestModel); 506 507 HttpRequestParametersHashModel requestParametersModel = 509 (HttpRequestParametersHashModel) request.getAttribute( 510 ATTR_REQUEST_PARAMETERS_MODEL); 511 params.putUnlistedModel(KEY_REQUEST_PARAMETERS, requestParametersModel); 512 return params; 513 } catch (ServletException e) { 514 throw new TemplateModelException(e); 515 } catch (IOException e) { 516 throw new TemplateModelException(e); 517 } 518 } 519 520 529 protected String requestUrlToTemplatePath(HttpServletRequest request) 530 { 531 String includeServletPath = (String ) request.getAttribute("javax.servlet.include.servlet_path"); 533 if(includeServletPath != null) 534 { 535 String includePathInfo = (String ) request.getAttribute("javax.servlet.include.path_info"); 538 return includePathInfo == null ? includeServletPath : includePathInfo; 539 } 540 String path = request.getPathInfo(); 544 if (path != null) return path; 545 path = request.getServletPath(); 546 if (path != null) return path; 547 return ""; 549 } 550 551 561 protected boolean preprocessRequest( 562 HttpServletRequest request, 563 HttpServletResponse response) 564 throws ServletException , IOException { 565 return false; 566 } 567 568 576 protected Configuration createConfiguration() { 577 return new Configuration(); 578 } 579 580 592 protected ObjectWrapper createObjectWrapper() { 593 String wrapper = getServletConfig().getInitParameter(DEPR_INITPARAM_OBJECT_WRAPPER); 594 if (wrapper != null) { if (getInitParameter(Configurable.OBJECT_WRAPPER_KEY) != null) { 596 throw new RuntimeException ("Conflicting init-params: " 597 + Configurable.OBJECT_WRAPPER_KEY + " and " 598 + DEPR_INITPARAM_OBJECT_WRAPPER); 599 } 600 if (DEPR_INITPARAM_WRAPPER_BEANS.equals(wrapper)) { 601 return ObjectWrapper.BEANS_WRAPPER; 602 } 603 if(DEPR_INITPARAM_WRAPPER_SIMPLE.equals(wrapper)) { 604 return ObjectWrapper.SIMPLE_WRAPPER; 605 } 606 if(DEPR_INITPARAM_WRAPPER_JYTHON.equals(wrapper)) { 607 try { 609 return (ObjectWrapper) Class.forName("freemarker.ext.jython.JythonWrapper") 610 .newInstance(); 611 } catch (InstantiationException e) { 612 throw new InstantiationError (e.getMessage()); 613 } catch (IllegalAccessException e) { 614 throw new IllegalAccessError (e.getMessage()); 615 } catch (ClassNotFoundException e) { 616 throw new NoClassDefFoundError (e.getMessage()); 617 } 618 } 619 return ObjectWrapper.DEFAULT_WRAPPER; 621 } else { 622 wrapper = getInitParameter(Configurable.OBJECT_WRAPPER_KEY); 623 if (wrapper == null) { 624 return ObjectWrapper.DEFAULT_WRAPPER; 626 } else { 627 try { 628 config.setSetting(Configurable.OBJECT_WRAPPER_KEY, wrapper); 629 } catch (TemplateException e) { 630 throw new RuntimeException (e.toString()); 631 } 632 return config.getObjectWrapper(); 633 } 634 } 635 } 636 637 protected ObjectWrapper getObjectWrapper() { 638 return wrapper; 639 } 640 641 protected final String getTemplatePath() { 642 return templatePath; 643 } 644 645 protected HttpRequestParametersHashModel createRequestParametersHashModel(HttpServletRequest request) { 646 return new HttpRequestParametersHashModel(request); 647 } 648 649 658 protected void initializeServletContext( 659 HttpServletRequest request, 660 HttpServletResponse response) 661 throws ServletException , IOException { 662 } 663 664 674 protected void initializeSession( 675 HttpServletRequest request, 676 HttpServletResponse response) 677 throws ServletException , IOException 678 { 679 } 680 681 693 protected boolean preTemplateProcess( 694 HttpServletRequest request, 695 HttpServletResponse response, 696 Template template, 697 TemplateModel data) 698 throws ServletException , IOException 699 { 700 return true; 701 } 702 703 713 protected void postTemplateProcess( 714 HttpServletRequest request, 715 HttpServletResponse response, 716 Template template, 717 TemplateModel data) 718 throws ServletException , IOException 719 { 720 } 721 722 727 protected Configuration getConfiguration() { 728 return config; 729 } 730 731 735 private void setBrowserCachingPolicy(HttpServletResponse res) 736 { 737 if (nocache) 738 { 739 res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, " 741 + "post-check=0, pre-check=0"); 742 res.setHeader("Pragma", "no-cache"); 744 res.setHeader("Expires", EXPIRATION_DATE); 746 } 747 } 748 } 749 | Popular Tags |