没有HTTP的Tomcat JSP / JSTL [英] Tomcat JSP/JSTL without HTTP

查看:96
本文介绍了没有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); 
类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调用您未计划的内容,并且,正如Santa所说,


哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈台哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈拉b

因此,对于所有反对者......

  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){
抛出新的RuntimeException(ex);
} catch(ClassNotFoundException ex){
抛出新的RuntimeException(ex);
} catch(InstantiationException ex){
抛出新的RuntimeException(ex);
} catch(IllegalAccessException ex){
throw new RuntimeException(ex);
} catch(ServletException ex){
抛出新的RuntimeException(ex);
} catch(IOException ex){
抛出新的RuntimeException(ex);
}
}

惊人的源代码和半小时调试器会为你做的。



我在/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容器的类路径上,这样容器就可以找到生成的类。



I加载类,并获取一个实例。



最后,我创建了MyRequest,MyRequest,最终创建了MySession。我的IDE方便地为各个接口创建了存根。在这种情况下,我实现了:MyRequest.getSession(),MyResponse.setContentType(),MyResponse.setBufferSize()和MyResponse.getWriter()。

  public PrintWriter getWriter()抛出IOException {
if(sw == null){
sw = new StringWriter();
pw = new PrintWriter(sw);
}
返回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。


I have a pretty standard web app running under Tomcat 7.

What I'm now trying to do is leverage JSP/JSTL as a templating language independent of the HTTP/web serving aspects of Tomcat to produce HTML that can be emailed and converted to PDF.

Has anyone else tried to do this before and could help me with some pointers?

Thanks in advance.

解决方案

In contrast to what Stephen C said, yes, JSP are Servlets, etc. etc. (And Velocity is quite good and easy to use)

But, what is a Servlet?

It's an interface. An interface with one major method:

service(ServletRequest req, ServletResponse res)

Locate the JSP class, cast it to a Servlet, create implementations of ServletRequest and ServletResponse, and then...

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();

Will this work? Well, after some work it will. Obviously you need to implement the minimum facades of the ServletRequest/Response as well as what ever your JSPs will need. But, likely you will probably need little more than the attributes and the streams. If you make your Response return a StringWriter, you're halfway there.

The next part is creating the servlet from the JSP. Handily, the Jasper compiler does that for you -- the game is invoking it. I have never done it directly, but it clearly can be done since both the servlet container does it, as well as the JSPC script/bat file, the ant task, as well as most of the Servlet containers out there use Jasper. So, that can be done. Once you know how to invoke that, you'll know the final generated class name for the JSP. (See the first line of the sample.)

Have I ever done this? No. But I bet within less than a day of messing around you'll know whether this is doable or not. I'm betting it is, especially if you don't run in to any class loader shenanigans. You'll possibly have an issue if you let your users change and regenerate a JSP (so MyEmail.jsp gets compiled in to MyEmail.class, MyEmail_2.class, etc.). But if you invoke Jasper yourself, you'll likely have more control over this. The other hard part is determining the class name of the JSP. Most of the containers follow a basic pattern here, so if you poke around in the generated code from a WAR you'll likely find it.

Keep the JSPs reasonably simple (and an Email template shouldn't need to super complicated with embedded Java or anything making random calls), and it even more a good chance it will work.

Your solution may not be portable out of the box out of Tomcat, but you likely won't care. The folks that I've talked to that use JSP for templates, simply opened a socket to their own server and made a request. They didn't go this far either.

But on the surface, save some whacky class loader black hole hell, I bet you can get this to work pretty quick. Implement as little of the request and response as you need to, fight a few NPEs as the JSP and JSTL call stuff you weren't planning, and, as Santa says,

Hack away, Hack away, Hack away all!

Addenda:

So, for all the naysayers...

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);
    }
}

Amazing what source code and 1/2 hour in a debugger will do for you.

I created a simple JSP in /tmp/app/newjsp.jsp.

jspc.setUriroot tells the compiler where the base of your "web app" is located. jspc.setOutputDir tells jspc where to put the generated Java and Class files. jspc.setJspFiles tells jspc what files to compile, based off of the URI Root. jspc.setCompile told it to actually compile the code. Finally, jspc.execute() does the deed.

By default Jasper uses the package org.apache.jsp, and creates a new class based on the JSP file name. For my simple experiment, I simply put "/tmp/dest" on to the class path of my Glassfish container, so that the container would find the generated classes.

I load the class, and get an instance.

Finally, I created MyRequest, MyRequest, and, ultimately, MySession. My IDE conveniently created stubs for the respective interfaces. In this case I implemented: MyRequest.getSession(), MyResponse.setContentType(), MyResponse.setBufferSize(), and MyResponse.getWriter().

public PrintWriter getWriter() throws IOException {
    if (sw == null) {
        sw = new StringWriter();
        pw = new PrintWriter(sw);
    }
    return pw;
}

Obviously sw and pw are instance variables of MyResponse.

MyRequest returned an instance of MySession. My implementation of MySession does -- nothing. But the runtime wanted a Session, it just doesn't use it on its own for my very simple JSP, and I wasn't motivated on stuffing in the the one from the Servlet.

I tested this on Glassfish v2.1. I simply added appserv_rt.jar (from glassfish/lib) to my build class path (so it could find the JspC jars), but I don't bundle it in the WAR (since it's already in the container).

And, shazam, it worked. In "real life", assuming the process that wanted to leverage the JSP was actually sourced from a web request, I would simply create an HttpServletResponseWrapper and override the earlier three methods, the rest would probably Just Work. If a web request isn't in the picture at all, then you'd need to create your own Session implementation (no big deal really, it's just a map).

I'd also use a private URLClassLoader to load the faux JSP classes. If I KNEW I'd never reload a JSP, then would just make the destination my WEB-INF/classes directory and give it it's own package and let the system load them.

But, yup, it worked. No big deal. It's just java.

这篇关于没有HTTP的Tomcat JSP / JSTL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆