没有HTTP的Tomcat JSP / JSTL

我有一个在Tomcat 7下运行的非常标准的Web应用程序。

我现在要做的是利用JSP / JSTL作为独立于Tomcat的HTTP / Web服务方面的模板语言来生成可以通过电子邮件发送并转换为PDF的HTML。

有没有其他人试图这样做,可以帮助我一些指示?

提前致谢。

与Stephen C所说的相反,是的,JSP是Servlets等等(而且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作为模板,只需打开一个套接字到他们自己的服务器并发出请求。 他们也没走这么远。

但从表面上看,保存一些糟糕的类装载机黑洞地狱,我敢打赌,你可以很快地使用它。 尽可能少地执行请求和响应,打几个NPE作为JSP和JSTL调用你没有计划的东西,并且正如圣诞老人所说,

劈开,劈开,劈开所有!

附加物:

所以,对于所有的反对者……

 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 app”的基础所在的位置。 jspc.setOutputDir告诉jspc将生成的Java和Class文件放在何处。 jspc.setJspFiles根据URI Root告诉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并覆盖之前的三种方法,其余的可能就是Just Work。 如果Web请求根本不在图片中,那么您需要创建自己的Session实现(真的没什么大不了的,它只是一张地图)。

我还使用私有URLClassLoader来加载虚假的JSP类。 如果我知道我永远不会重新加载JSP,那么只需将目标作为我的WEB-INF / classes目录并给它自己的包并让系统加载它们。

但是,是的,它有效。 没什么大不了。 这只是java。

这没有多大意义。 JSP是一种很好的语法,可以生成Java EE servlet类。 实际上,JSP的“servlet”/“http”特性通过API和JSP和JSTL的语义模型完全交织在一起。

如果您想独立于Web请求生成HTML,那么最好使用不同的模板技术; 例如Velocity或FreeMarker。 如果您希望HTML也作为Web响应传递,请安排您的servlet调用模板引擎来生成响应。 (如果您使用的是Spring,那么现有的基础架构就可以了。其他框架可能有类似的支持,但如果没有,那么自己实现一些粘合代码并不困难。)