我有一个在Tomcat 7下运行的标准Web应用程序。
我现在想做的是利用JSP / JSTL作为一种模板语言,独立于Tomcat的HTTP / Web服务方面,以生成可以通过电子邮件发送并转换为PDF的HTML。
之前有没有其他人尝试过这样做并且可以帮助我提供一些建议?
提前致谢。
与Stephen C所说的相反,是的,JSP是Servlet等,等等(并且Velocity非常好并且易于使用)。
但是,什么是Servlet?
这是一个界面。具有一种主要方法的接口:
service(ServletRequest req, ServletResponse res)
找到JSP类,将其转换为Servlet,创建ServletRequest和ServletResponse的实现,然后…
String jspClassName = findJspClassForJSP("your.jsp"); Class jspClass = Class.forName(jspClassName); Servlet jspServlet = (Servlet)jspClass.newInstance(); MyServletRequest req = new MyServletRequest(); MyServletResponse resp = new MyServletResponse(); jspServlet.init(); jspServlet.service(req, resp); jspServlet.destroy(); String results = reps.getContent();
这样行吗?好吧,经过一些工作,它会。显然,您需要实现ServletRequest / Response的最小外观以及您的JSP所需的外观。但是,可能您可能只需要属性和流。如果您使Response返回StringWriter,那么您就在中间。
下一部分是从JSP创建servlet。Jasper编译器可以轻松地为您做到这一点- 游戏正在调用它。我从来没有直接做过,但是显然可以做到,因为servlet容器,JSPC脚本/ bat文件,ant任务以及其中的大多数Servlet容器都使用Jasper。因此,可以做到。一旦知道如何调用它,便会知道最终为JSP生成的类名称。(请参阅示例的第一行。)
我曾经做过吗?不会。但是我敢打赌,在不到一天的时间里,您就会知道这是否可行。我敢打赌,尤其是如果您没有遇到任何类加载程序的恶作剧。如果让您的用户更改并重新生成JSP,则可能会遇到问题(因此MyEmail.jsp被编译为MyEmail.class,MyEmail_2.class等)。但是,如果您自己调用Jasper,则可能对此具有更多控制权。另一个困难的部分是确定JSP的类名。大多数容器在这里都遵循基本模式,因此,如果您在WAR中生成的代码中四处查找,您很可能会找到它。
保持JSP相当简单(并且电子邮件模板不需要与嵌入式Java或进行随机调用的任何东西一起变得非常复杂),它很有可能会工作。
您的解决方案可能无法从Tomcat的开箱即用的地方进行移植,但是您可能并不在意。我与之交谈的人使用JSP作为模板,只是打开了自己服务器的套接字并发出了请求。他们也没有走这么远。
但是从表面上看,省去了一些怪异的类加载器黑洞,我敢打赌,您可以使它快速运行。实现您所需的尽可能少的请求和响应,与您计划中未打算进行的JSP和JSTL调用工作打交道的NPE,并且,正如圣诞老人所说,
砍掉,砍掉,砍掉所有!
附加物:
所以,对于所有反对者…
public void runJsp() { JspC jspc = new JspC(); jspc.setUriroot("/tmp/app"); jspc.setOutputDir("/tmp/dest"); jspc.setJspFiles("newjsp.jsp"); jspc.setCompile(true); try { jspc.execute(); Class cls = Class.forName("org.apache.jsp.newjsp_jsp"); Servlet s = (Servlet) cls.newInstance(); MyRequest req = new MyRequest(); MyResponse resp = new MyResponse(); s.init(getServletConfig()); s.service(req, resp); s.destroy(); System.out.println(resp.getSw().toString()); } catch (JasperException ex) { throw new RuntimeException(ex); } catch (ClassNotFoundException ex) { throw new RuntimeException(ex); } catch (InstantiationException ex) { throw new RuntimeException(ex); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } catch (ServletException ex) { throw new RuntimeException(ex); } catch (IOException ex) { throw new RuntimeException(ex); } }
令人惊讶的是,在调试器中需要什么源代码和1/2个小时才能为您完成。
我在/tmp/app/newjsp.jsp中创建了一个简单的JSP。
jspc.setUriroot告诉编译器“ Web应用程序”的基础位置。jspc.setOutputDir告诉jspc将生成的Java和Class文件放在何处。jspc.setJspFiles根据URI根告诉jspc编译哪些文件。jspc.setCompile告诉它实际编译代码。最后,jspc.execute()做事。
默认情况下,Jasper使用软件包org.apache.jsp,并基于JSP文件名创建一个新类。对于我的简单实验,我只是将“ / tmp / dest”放在我的Glassfish容器的类路径上,以便该容器可以找到生成的类。
我加载该类,并获取一个实例。
最后,我创建了MyRequest,MyRequest,最后创建了MySession。我的IDE方便地为各个接口创建了存根。在这种情况下,我实现了:MyRequest.getSession(),MyResponse.setContentType(),MyResponse.setBufferSize()和MyResponse.getWriter()。
public PrintWriter getWriter() throws IOException { if (sw == null) { sw = new StringWriter(); pw = new PrintWriter(sw); } return pw; }
显然,sw和pw是MyResponse的实例变量。
MyRequest返回了MySession的实例。我对MySession的实现没有任何作用。但是运行时需要一个Session,只是我自己非常简单的JSP并没有单独使用它,并且我也没有动机将其填充到Servlet中。
我在Glassfish v2.1上对此进行了测试。我只是将appserv_rt.jar(来自glassfish / lib)添加到了我的构建类路径中(因此它可以找到JspC jar),但是我没有将其捆绑在WAR中(因为它已经在容器中了)。
而且,shazam奏效了。在“现实生活”中,假设想要利用JSP的过程实际上是从Web请求中获取的,我将简单地创建一个HttpServletResponseWrapper并覆盖前面的三种方法,其余的方法可能都可以正常工作。如果根本没有Web请求,那么您需要创建自己的Session实现(实际上没什么大不了的,它只是一张地图)。
我还将使用私有URLClassLoader来加载伪JSP类。如果我知道我永远不会重新加载JSP,那么只需将目标位置设置为WEB-INF / classes目录,并为其提供自己的软件包,然后由系统加载它们即可。
但是,是的,它有效。没什么大不了的。这只是java。