Tomcat JSP / JSTL without HTTP

I have a pretty standard web application under Tomcat 7.

What I'm trying to do now is to use JSP / JSTL as a template language independent of Tomcat's HTTP / web services to create 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.

+4
source share
2 answers

Unlike what Stephen C said, yes, JSPs are Servlets, etc. etc. (and Velocity is pretty good and easy to use)

But what is a servlet?

This is the interface. Interface with one main method:

service(ServletRequest req, ServletResponse res) 

Find the JSP class, pass it to the servlet, create the 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 be. Obviously, you need to implement the minimal ServletRequest / Response facades, as well as what your JSP will need. But most likely you will probably need a little more than attributes and threads. If you return the answer, StringWriter, you're halfway there.

The next part creates a servlet from JSP. Conveniently, the Jasper compiler does this for you - the game calls it. I have never done this directly, but it can be done explicitly, since this is done by the servlet container, as well as the JSPC script / bat file, the ant task, and also most Servlet containers there use Jasper. So it can be done. When you know how to call this, you will find out the final name of the generated class for the JSP. (See the first line of the sample.)

Have I ever done this? No. But I bet for less than one day of clutter around, you find out whether this is doable or not. I bet this is the case, especially if you don't get into any shenanigans cool downloader. You may have a problem if you allow your users to modify and restore the JSP (therefore MyEmail.jsp is compiled into MyEmail.class, MyEmail_2.class, etc.). But if you call Jasper on your own, you will likely have more control over this. Another tricky part is defining a JSP class name. Most containers follow the basic pattern here, so if you poke in the generated code from WAR, you will most likely find it.

Keep the JSP simple enough (and the email template should not be too complicated with embedded Java or anything that makes random calls), and this is an even better chance that it will work.

Your solution may not be portable out of the Tomcat box, but you probably don't care. The people I talked to use JSP for templates, just opened the socket on their own server and made a request. They did not leave either.

But on the surface, save some kind of awesome cool class black hole addon, I'm sure you can get this to work pretty quickly. Implement as few requests and responses as you need, fight several NPEs like JSP and JSTL that you did not plan, and as Santa says,

Hack, hack, hack everything!

Addenda:

So, for all skeptics ...

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

It's amazing what the source code and 1/2 hour in the 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 application" is located. jspc.setOutputDir tells jspc where to put the generated Java and Class files. jspc.setJspFiles tells jspc which files to compile based on the root of the URI. jspc.setCompile said that it really compiles the code. Finally, jspc.execute () does the job.

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

I load the class and get an instance.

Finally, I created MyRequest, MyRequest and, ultimately, MySession. In my IDE, stubs were created 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 MySession implementation is nothing. But the runtime required a session, it just did not use it on its own for my very simple JSP, and I was not motivated to stuff in that of a servlet.

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

And, shazam, it worked. In "real life", assuming that the process that wanted to use the JSP was actually derived from a web request, I simply created an HttpServletResponseWrapper and redefined the previous three methods, the rest probably just worked. If the web request is not present at all in the image, you will need to create your own session implementation (it doesn't matter, it's just a map).

I would also use a private URLClassLoader to load fake JSP classes. If I UNDERSTAND, I would never restart JSP, but just make the destination my WEB-INF / classes directory and provide it with my own package, and let the system load them.

But, yes, it worked. Nothing wrong. This is just java.

+8
source

It does not make sense. JSP is a good syntax that generates a Java EE servlet class. Indeed, the "servlet" / "http" nature of the JSP is thoroughly interwoven through the API and the semantic model of JSP and JSTL.

If you want to generate HTML regardless of web requests, you are better off using a different template technology; e.g. Velocity or FreeMarker. If you want HTML to be delivered as web responses, as well as your servlets invoking a template engine to generate responses. (If you use Spring, there is an existing infrastructure for this. Other frameworks may have similar support, but if not, it should not be difficult to implement any code to create this.)

+3
source

Source: https://habr.com/ru/post/1386625/


All Articles